引言
Android 系统休眠,常见的接口是PowerManager提供的goToSleep,但是很遗憾,调用这个接口需要System 权限;即用Platform key签名的APP才可以;
那么普通APP 可否使得系统suspend呢?
lockNow函数的作用
lockNow定义于文件DevicePolicyManager.java中,实现如下:
mService即IDevicePolicyManager,由DevicePolicyManagerService实现;
public void lockNow() {
if (mService != null) {
try {
mService.lockNow();
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
}
}
mService即IDevicePolicyManager,由DevicePolicyManagerService实现;
所以lockNow的具体实现在DevicePolicyManagerService中
public void lockNow() {
if (!mHasFeature) {
return;
}
synchronized (this) {
// This API can only be called by an active device admin,
// so try to retrieve it to check that the caller is one.
getActiveAdminForCallerLocked(null,
DeviceAdminInfo.USES_POLICY_FORCE_LOCK);
lockNowUnchecked();
}
}
private void lockNowUnchecked() {
long ident = Binder.clearCallingIdentity();
try {
// Power off the display
mPowerManager.goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN, 0);
// Ensure the device is locked
new LockPatternUtils(mContext).requireCredentialEntry(UserHandle.USER_ALL);
getWindowManager().lockNow(null);
} catch (RemoteException e) {
} finally {
Binder.restoreCallingIdentity(ident);
}
}
getActiveAdminForCallerLocked检查当前的调用者是否为Device admin,如果是的,则执行lockNowUnchecked,否则会在getActiveAdminForCallerLocked执行过程中抛出SecurityException;
而lockNowUnchecked函数实际上调用了goToSleep使得系统suspend;
总的来说,lockNow是对goToSleep降低权限的封装,所以我们可以通过调用lockNow来实现系统suspend
普通APP如何使得系统休眠
在Android系统中,APP根据签名不同分为system app或者普通app,system app使用platform key签名,具备system权限,可以做一些系统级别的操作,例如调用goToSleep,调用reboot等;普通APP即用没有用哦platform key签名的app;那么这样的APP 如何实现系统休眠呢?我们刚刚提到的lockNow函数就是一种办法;但是问题是,嗲用lockNow之前得先具备device admin权限;
普通APP如何成为device admin
1.. 在AndroidManifest.xml中添加receiver类继承自DeviceAdminReceiver
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.suspendappnormal.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name="com.example.suspendappnormal.MyAdmin"
android:label="@string/app_name"
android:permission="android.permission.BIND_DEVICE_ADMIN" >
<meta-data
android:name="android.app.device_admin"
android:resource="@xml/device_admin" />
<intent-filter>
<action
android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
</application>
MyAdmin即为添加的receiver
2.. 为MyAdmin添加java code
package com.example.suspendappnormal;
import android.app.admin.DeviceAdminReceiver;
public class MyAdmin extends DeviceAdminReceiver {
}
很简单,就是一个继承自DeviceAdminReceiver的类,不用实现任何函数
3.. 在Activity中实现device admin的添加
activity_main.xml文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.suspendappnormal.MainActivity" >
<Button
android:id="@+id/admin_add_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="AddToAdminDevice"/>
<Button
android:id="@+id/suspend_btn"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="suspend"/>
</LinearLayout>
id 为admin_add_btn的button,做 device admin的添加,如果添加成功之后,该按钮编程灰色,不可点击;如果已经具备device admin权限,该按钮也是灰色,不可点击;
id为suspend_btn的Button,完成lockNow函数的执行;
onCreate函数
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAdminName = new ComponentName(this, MyAdmin.class);
mDm = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
mAdminAddBtn = (Button)findViewById(R.id.admin_add_btn);
mAdminAddBtn.setOnClickListener(this);
mSuspendBtn = (Button)findViewById(R.id.suspend_btn);
mSuspendBtn.setOnClickListener(this);
if (!mDm.isAdminActive(mAdminName)) {
mAdminAddBtn.setEnabled(true);
} else {
mAdminAddBtn.setEnabled(false);
}
}
new 出mAdminName的component组件,就是为该组件所在的process添加admin权限;
点击AddToAdminDevice button,则添加admin权限,button的click相应事件如下:
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch(v.getId()) {
case R.id.admin_add_btn:
if (mDm != null) {
if (!mDm.isAdminActive(mAdminName)) {
Log.i(TAG, "has no admin, add it");
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra (DevicePolicyManager.EXTRA_DEVICE_ADMIN, mAdminName);
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "sara sara sara");
startActivityForResult(intent, 0);
mAdminAddBtn.setEnabled(false);
}
}
break;
case R.id.suspend_btn:
if (mDm != null) {
if (mDm.isAdminActive(mAdminName)) {
Log.i(TAG, "lockNow");
mDm.lockNow();
} else {
Log.i(TAG, "admin not active");
}
}
break;
default:
break;
}
}
4.. 运行效果如下:
第一次运行该APP时:
点击ADDTOADMIN button
点击Active
此时ADDTOADMIN button是disable的,因为此时APP已经获得了admin权限;
点击Suspend即会使得系统suspend
结束语
device admin权限,只用申请一次,该APP就一直拥有,所以不用每次调用lockNow时,都去为该APP申请;
此外Android系统中,admin权限的管理在Setting app中;
Setting->security->Device Adminstrators;
如果要删除具备admin权限的APP,需要先到Setting中,将该权限移除;否则无法删除该APP;