Add SettingsManager.

Add new APIs for changing a subset of system settings.

Protected by cyanogenmod.permission.MODIFY_NETWORK_SETTINGS:
    - Add ability to toggle airplane mode on/off.
    - Add ability to toggle mobile data on/off.

Protected by android.permission.REBOOT:
    - Add ability to shutdown or reboot the device.

Change-Id: I5e943be11260c58afa664f1702c0ecb4413528fe
This commit is contained in:
Matt Garnes 2015-07-14 16:29:56 -07:00
parent 5b61a21f7f
commit 0cdb1d513c
11 changed files with 440 additions and 0 deletions

View File

@ -236,6 +236,14 @@ package cyanogenmod.app {
field public static final int PROFILES_STATE_ENABLED = 1; // 0x1
}
public class SettingsManager {
method public static cyanogenmod.app.SettingsManager getInstance(android.content.Context);
method public void rebootDevice();
method public void setAirplaneModeEnabled(boolean);
method public void setMobileDataEnabled(boolean);
method public void shutdownDevice();
}
public class StatusBarPanelCustomTile implements android.os.Parcelable {
ctor public StatusBarPanelCustomTile(java.lang.String, java.lang.String, java.lang.String, int, java.lang.String, int, int, cyanogenmod.app.CustomTile, android.os.UserHandle);
ctor public StatusBarPanelCustomTile(java.lang.String, java.lang.String, java.lang.String, int, java.lang.String, int, int, cyanogenmod.app.CustomTile, android.os.UserHandle, long);
@ -285,6 +293,7 @@ package cyanogenmod.platform {
public static final class Manifest.permission {
ctor public Manifest.permission();
field public static final java.lang.String MODIFY_SETTINGS = "cyanogenmod.permission.MODIFY_SETTINGS";
field public static final java.lang.String PUBLISH_CUSTOM_TILE = "cyanogenmod.permission.PUBLISH_CUSTOM_TILE";
}

View File

@ -0,0 +1,158 @@
/*
* Copyright (c) 2011-2015 CyanogenMod Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.cyanogenmod.platform.internal;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.IBinder;
import android.os.IPowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.Log;
import com.android.server.SystemService;
import cyanogenmod.app.CMContextConstants;
import cyanogenmod.app.ISettingsManager;
import cyanogenmod.app.SettingsManager;
import java.io.ByteArrayInputStream;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
/** {@hide} */
public class SettingsManagerService extends SystemService {
private static final String TAG = "CMSettingsService";
private Context mContext;
private TelephonyManager mTelephonyManager;
public SettingsManagerService(Context context) {
super(context);
mContext = context;
publishBinderService(CMContextConstants.CM_SETTINGS_SERVICE, mService);
}
@Override
public void onStart() {
mTelephonyManager = (TelephonyManager)
mContext.getSystemService(Context.TELEPHONY_SERVICE);
}
private void enforceModifyNetworkSettingsPermission() {
mContext.enforceCallingOrSelfPermission(SettingsManager.MODIFY_NETWORK_SETTINGS_PERMISSION,
"You do not have permissions to change system network settings.");
}
private void enforceShutdownPermission() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT,
"You do not have permissions to shut down the device.");
}
private final IBinder mService = new ISettingsManager.Stub() {
@Override
public void setAirplaneModeEnabled(boolean enabled) {
enforceModifyNetworkSettingsPermission();
/*
* We need to clear the caller's identity in order to
* allow this method call to modify settings
* not allowed by the caller's permissions.
*/
long token = clearCallingIdentity();
setAirplaneModeEnabledInternal(enabled);
restoreCallingIdentity(token);
}
@Override
public void setMobileDataEnabled(boolean enabled) {
enforceModifyNetworkSettingsPermission();
/*
* We need to clear the caller's identity in order to
* allow this method call to modify settings
* not allowed by the caller's permissions.
*/
long token = clearCallingIdentity();
setMobileDataEnabledInternal(enabled);
restoreCallingIdentity(token);
}
@Override
public void shutdown() {
enforceShutdownPermission();
/*
* We need to clear the caller's identity in order to
* allow this method call to modify settings
* not allowed by the caller's permissions.
*/
long token = clearCallingIdentity();
shutdownInternal(false);
restoreCallingIdentity(token);
}
@Override
public void reboot() {
enforceShutdownPermission();
/*
* We need to clear the caller's identity in order to
* allow this method call to modify settings
* not allowed by the caller's permissions.
*/
long token = clearCallingIdentity();
shutdownInternal(true);
restoreCallingIdentity(token);
}
};
private void setAirplaneModeEnabledInternal(boolean enabled) {
// Change the system setting
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON,
enabled ? 1 : 0);
// Post the intent
Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intent.putExtra("state", enabled);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
private void setMobileDataEnabledInternal(boolean enabled) {
mTelephonyManager.setDataEnabled(enabled);
}
private void shutdownInternal(boolean reboot) {
IPowerManager pm = IPowerManager.Stub.asInterface(
ServiceManager.getService(Context.POWER_SERVICE));
try {
if (reboot) {
pm.reboot(false, null, false);
} else {
pm.shutdown(false, false);
}
} catch (RemoteException e) {
Log.d(TAG, "Unable to shutdown.");
}
}
}

View File

@ -35,6 +35,13 @@
android:icon="@drawable/ic_launcher_cyanogenmod"
android:protectionLevel="normal" />
<!-- Allows system applications to make changes to a subset of system network settings -->
<permission android:name="cyanogenmod.permission.MODIFY_NETWORK_SETTINGS"
android:label="@string/permlab_modifyNetworkSettings"
android:description="@string/permdesc_modifyNetworkSettings"
android:icon="@drawable/ic_launcher_cyanogenmod"
android:protectionLevel="system|signature" />
<application android:process="system"
android:persistent="true"
android:hasCode="false"

View File

@ -21,6 +21,9 @@
<string name="permlab_publishCustomTile">create a custom tile within quick settings panel</string>
<string name="permdesc_publishCustomTile">Allows an app to publish a quick settings tile.</string>
<string name="permlab_modifyNetworkSettings">change system network settings</string>
<string name="permdesc_modifyNetworkSettings">Allows an app to make changes to a restricted set of system network settings.</string>
<string name="permlab_bindCustomTileListenerService">bind to a custom tile listener service</string>
<string name="permdesc_bindCustomTileListenerService">Allows the app to bind to the top-level interface of a custom tile listener service.</string>

View File

@ -50,4 +50,15 @@ public final class CMContextConstants {
* @hide
*/
public static final String CM_PROFILE_SERVICE = "profile";
/**
* Use with {@link android.content.Context#getSystemService} to retrieve a
* {@link cyanogenmod.app.SettingsManager} changing system settings.
*
* @see android.content.Context#getSystemService
* @see cyanogenmod.app.SettingsManager
*
* @hide
*/
public static final String CM_SETTINGS_SERVICE = "cmsettings";
}

View File

@ -0,0 +1,27 @@
/* //device/java/android/android/app/IProfileManager.aidl
**
** Copyright (C) 2015 The CyanogenMod Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
package cyanogenmod.app;
/** {@hide} */
interface ISettingsManager
{
void setAirplaneModeEnabled(boolean enabled);
void setMobileDataEnabled(boolean enabled);
void shutdown();
void reboot();
}

View File

@ -0,0 +1,152 @@
/*
* Copyright (C) 2015 The CyanogenMod Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cyanogenmod.app;
import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
/**
* <p>
* The SettingsManager allows applications to modify a subset of system settings.
* </p>
*/
public class SettingsManager {
private static ISettingsManager sService;
private Context mContext;
/**
* Allows an application to change network settings,
* such as enabling or disabling airplane mode.
*/
public static final String MODIFY_NETWORK_SETTINGS_PERMISSION =
"cyanogenmod.permission.MODIFY_NETWORK_SETTINGS";
private static final String TAG = "SettingsManager";
private static SettingsManager sSettingsManagerInstance;
private SettingsManager(Context context) {
Context appContext = context.getApplicationContext();
if (appContext != null) {
mContext = appContext;
} else {
mContext = context;
}
sService = getService();
}
/**
* Get or create an instance of the {@link cyanogenmod.app.SettingsManager}
* @param context
* @return {@link SettingsManager}
*/
public static SettingsManager getInstance(Context context) {
if (sSettingsManagerInstance == null) {
sSettingsManagerInstance = new SettingsManager(context);
}
return sSettingsManagerInstance;
}
/** @hide */
static public ISettingsManager getService() {
if (sService != null) {
return sService;
}
IBinder b = ServiceManager.getService(CMContextConstants.CM_SETTINGS_SERVICE);
if (b != null) {
sService = ISettingsManager.Stub.asInterface(b);
return sService;
} else {
return null;
}
}
/**
* Turns on or off airplane mode.
*
* You will need {@link #MODIFY_NETWORK_SETTINGS_PERMISSION}
* to utilize this functionality.
* @param enabled if true, sets airplane mode to enabled, otherwise disables airplane mode.
*/
public void setAirplaneModeEnabled(boolean enabled) {
if (sService == null) {
return;
}
try {
getService().setAirplaneModeEnabled(enabled);
} catch (RemoteException e) {
Log.e(TAG, e.getLocalizedMessage(), e);
}
}
/**
* Turns on or off mobile network.
*
* You will need {@link #MODIFY_NETWORK_SETTINGS_PERMISSION}
* to utilize this functionality.
* @param enabled if true, sets mobile network to enabled, otherwise disables mobile network.
*/
public void setMobileDataEnabled(boolean enabled) {
if (sService == null) {
return;
}
try {
getService().setMobileDataEnabled(enabled);
} catch (RemoteException e) {
Log.e(TAG, e.getLocalizedMessage(), e);
}
}
/**
* Shuts down the device, immediately.
*
* You will need {@link android.Manifest.permission.REBOOT}
* to utilize this functionality.
*/
public void shutdownDevice() {
if (sService == null) {
return;
}
try {
getService().shutdown();
} catch (RemoteException e) {
Log.e(TAG, e.getLocalizedMessage(), e);
}
}
/**
* Reboots the device, immediately.
*
* You will need {@link android.Manifest.permission.REBOOT}
* to utilize this functionality.
*/
public void rebootDevice() {
if (sService == null) {
return;
}
try {
getService().reboot();
} catch (RemoteException e) {
Log.e(TAG, e.getLocalizedMessage(), e);
}
}
}

View File

@ -236,6 +236,14 @@ package cyanogenmod.app {
field public static final int PROFILES_STATE_ENABLED = 1; // 0x1
}
public class SettingsManager {
method public static cyanogenmod.app.SettingsManager getInstance(android.content.Context);
method public void rebootDevice();
method public void setAirplaneModeEnabled(boolean);
method public void setMobileDataEnabled(boolean);
method public void shutdownDevice();
}
public class StatusBarPanelCustomTile implements android.os.Parcelable {
ctor public StatusBarPanelCustomTile(java.lang.String, java.lang.String, java.lang.String, int, java.lang.String, int, int, cyanogenmod.app.CustomTile, android.os.UserHandle);
ctor public StatusBarPanelCustomTile(java.lang.String, java.lang.String, java.lang.String, int, java.lang.String, int, int, cyanogenmod.app.CustomTile, android.os.UserHandle, long);
@ -285,6 +293,7 @@ package cyanogenmod.platform {
public static final class Manifest.permission {
ctor public Manifest.permission();
field public static final java.lang.String MODIFY_SETTINGS = "cyanogenmod.permission.MODIFY_SETTINGS";
field public static final java.lang.String PUBLISH_CUSTOM_TILE = "cyanogenmod.permission.PUBLISH_CUSTOM_TILE";
}

View File

@ -6,6 +6,8 @@
<uses-permission android:name="cyanogenmod.permission.PUBLISH_CUSTOM_TILE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<uses-permission android:name="cyanogenmod.permission.MODIFY_NETWORK_SETTINGS" />
<uses-permission android:name="android.permission.REBOOT" />
<application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
<uses-library android:name="android.test.runner" />
@ -16,6 +18,13 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".settings.CMSettingsManagerTest"
android:label="@string/settings_tests_activity_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".profiles.ProfileTest"
android:label="@string/app_name">
<intent-filter>

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">CM Platform Tests</string>
<string name="settings_tests_activity_name">CM Platform Settings Tests</string>
</resources>

View File

@ -0,0 +1,54 @@
package org.cyanogenmod.tests.settings;
import org.cyanogenmod.tests.TestActivity;
import cyanogenmod.app.SettingsManager;
/**
* Tests functionality added in {@link cyanogenmod.app.SettingsManager}
*/
public class CMSettingsManagerTest extends TestActivity {
SettingsManager mSettingsManager;
@Override
protected String tag() {
return null;
}
@Override
protected Test[] tests() {
mSettingsManager = SettingsManager.getInstance(this);
return mTests;
}
private Test[] mTests = new Test[] {
new Test("Test set airplane mode to on") {
public void run() {
mSettingsManager.setAirplaneModeEnabled(true);
}
},
new Test("Test set airplane mode to off") {
public void run() {
mSettingsManager.setAirplaneModeEnabled(false);
}
},
new Test("Test set mobile data to on") {
public void run() {
mSettingsManager.setMobileDataEnabled(true);
}
},
new Test("Test set mobile data to off") {
public void run() {
mSettingsManager.setMobileDataEnabled(false);
}
},
new Test("Test reboot the device") {
public void run() {
mSettingsManager.rebootDevice();
}
},
new Test("Test shutdown the device") {
public void run() {
mSettingsManager.shutdownDevice();
}
}
};
}