/* * 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.profiles; import android.bluetooth.BluetoothAdapter; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.location.LocationManager; import android.net.wifi.WifiManager; import android.net.wimax.WimaxHelper; import android.nfc.NfcAdapter; import android.os.Parcel; import android.os.Parcelable; import android.provider.Settings; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import com.android.internal.telephony.RILConstants; import cyanogenmod.os.Build; import cyanogenmod.os.Concierge; import cyanogenmod.os.Concierge.ParcelInfo; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; /** * The {@link ConnectionSettings} class allows for creating Network/Hardware overrides * depending on their capabilities. * *

Example for enabling/disabling sync settings: *

 * ConnectionSettings connectionSettings =
 *         new ConnectionSettings(ConnectionSettings.PROFILE_CONNECTION_SYNC,
 *         shouldBeEnabled() ?
 *         {@link BooleanState#STATE_ENABLED} : {@link BooleanState#STATE_DISALED},
 *         true)
 * profile.setConnectionSettings(connectionSettings);
 * 
*/ public final class ConnectionSettings implements Parcelable { private int mConnectionId; private int mValue; private boolean mOverride; private boolean mDirty; /** * For use with {@link #PROFILE_CONNECTION_2G3G4G} to determine what subscription to control. */ private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; /** * The {@link #PROFILE_CONNECTION_MOBILEDATA} allows for enabling and disabling the mobile * data connection. Boolean connection settings {@link BooleanState} */ public static final int PROFILE_CONNECTION_MOBILEDATA = 0; /** * The {@link #PROFILE_CONNECTION_WIFI} allows for enabling and disabling the WiFi connection * on the device. Boolean connection settings {@link BooleanState} */ public static final int PROFILE_CONNECTION_WIFI = 1; /** * The {@link #PROFILE_CONNECTION_WIFIAP} allows for enabling and disabling the WiFi hotspot * on the device. Boolean connection settings {@link BooleanState} */ public static final int PROFILE_CONNECTION_WIFIAP = 2; /** * The {@link #PROFILE_CONNECTION_WIMAX} allows for enabling and disabling the WIMAX radio (if exists) * on the device. Boolean connection settings {@link BooleanState} */ public static final int PROFILE_CONNECTION_WIMAX = 3; /** * The {@link #PROFILE_CONNECTION_GPS} allows for enabling and disabling the GPS radio (if exists) * on the device. Boolean connection settings {@link BooleanState} */ public static final int PROFILE_CONNECTION_GPS = 4; /** * The {@link #PROFILE_CONNECTION_SYNC} allows for enabling and disabling the global sync state * on the device. Boolean connection settings {@link BooleanState} */ public static final int PROFILE_CONNECTION_SYNC = 5; /** * The {@link #PROFILE_CONNECTION_BLUETOOTH} allows for enabling and disabling the Bluetooth device * (if exists) on the device. Boolean connection settings {@link BooleanState} */ public static final int PROFILE_CONNECTION_BLUETOOTH = 7; /** * The {@link #PROFILE_CONNECTION_NFC} allows for enabling and disabling the NFC device * (if exists) on the device. Boolean connection settings {@link BooleanState} */ public static final int PROFILE_CONNECTION_NFC = 8; /** * The {@link #PROFILE_CONNECTION_2G3G4G} allows for flipping between 2G/3G/4G (if exists) * on the device. */ public static final int PROFILE_CONNECTION_2G3G4G = 9; // retrieved from Phone.apk private static final String ACTION_MODIFY_NETWORK_MODE = "com.android.internal.telephony.MODIFY_NETWORK_MODE"; private static final String EXTRA_NETWORK_MODE = "networkMode"; private static final String EXTRA_SUB_ID = "subId"; /** * BooleanStates for specific {@link ConnectionSettings} */ public static class BooleanState { /** Disabled state */ public static final int STATE_DISALED = 0; /** Enabled state */ public static final int STATE_ENABLED = 1; } private static final int CM_MODE_2G = 0; private static final int CM_MODE_3G = 1; private static final int CM_MODE_4G = 2; private static final int CM_MODE_2G3G = 3; private static final int CM_MODE_ALL = 4; /** @hide */ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public ConnectionSettings createFromParcel(Parcel in) { return new ConnectionSettings(in); } @Override public ConnectionSettings[] newArray(int size) { return new ConnectionSettings[size]; } }; /** * Unwrap {@link ConnectionSettings} from a parcel. * @param parcel */ public ConnectionSettings(Parcel parcel) { readFromParcel(parcel); } /** * Construct a {@link ConnectionSettings} with a connection id and default states. * @param connectionId ex: #PROFILE_CONNECTION_NFC */ public ConnectionSettings(int connectionId) { this(connectionId, 0, false); } /** * Construct a {@link ConnectionSettings} with a connection id, default value * {@see BooleanState}, and if the setting should override the user defaults. * @param connectionId an identifier for the ConnectionSettings (ex:#PROFILE_CONNECTION_WIFI) * @param value default value for the ConnectionSettings (ex:{@link BooleanState#STATE_ENABLED}) * @param override whether or not the {@link ConnectionSettings} should override user defaults */ public ConnectionSettings(int connectionId, int value, boolean override) { mConnectionId = connectionId; mValue = value; mOverride = override; mDirty = false; } /** * Retrieve the connection id associated with the {@link ConnectionSettings} * @return an integer identifier */ public int getConnectionId() { return mConnectionId; } /** * Get the default value for the {@link ConnectionSettings} * @return integer value corresponding with its state */ public int getValue() { return mValue; } /** * Set the default value for the {@link ConnectionSettings} * @param value {@link BooleanState} */ public void setValue(int value) { mValue = value; mDirty = true; } /** * Set whether or not the {@link ConnectionSettings} should override default user values * @param override boolean override */ public void setOverride(boolean override) { mOverride = override; mDirty = true; } public void setSubId(int subId) { mSubId = subId; mDirty = true; } /** * Check whether or not the {@link ConnectionSettings} overrides user settings. * @return true if override */ public boolean isOverride() { return mOverride; } /** * Get the subscription id which this {@link ConnectionSettings} should apply to. * @return */ public int getSubId() { return mSubId; } /** @hide */ public boolean isDirty() { return mDirty; } /** @hide */ public void processOverride(Context context) { BluetoothAdapter bta = BluetoothAdapter.getDefaultAdapter(); LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); NfcAdapter nfcAdapter = null; try { nfcAdapter = NfcAdapter.getNfcAdapter(context); } catch (UnsupportedOperationException e) { //Nfc not available } boolean forcedState = getValue() == 1; boolean currentState; switch (getConnectionId()) { case PROFILE_CONNECTION_MOBILEDATA: currentState = tm.getDataEnabled(); if (forcedState != currentState) { int phoneCount = tm.getPhoneCount(); for (int i = 0; i < phoneCount; i++) { Settings.Global.putInt(context.getContentResolver(), Settings.Global.MOBILE_DATA + i, (forcedState) ? 1 : 0); int[] subId = SubscriptionManager.getSubId(i); tm.setDataEnabled(subId[0], forcedState); } } break; case PROFILE_CONNECTION_2G3G4G: if (Build.CM_VERSION.SDK_INT >= Build.CM_VERSION_CODES.ELDERBERRY) { Intent intent = new Intent(ACTION_MODIFY_NETWORK_MODE); intent.putExtra(EXTRA_NETWORK_MODE, getValue()); intent.putExtra(EXTRA_SUB_ID, getSubId()); context.sendBroadcast(intent, "com.android.phone.CHANGE_NETWORK_MODE"); } else { Intent intent = new Intent(ACTION_MODIFY_NETWORK_MODE); switch(getValue()) { case CM_MODE_2G: intent.putExtra(EXTRA_NETWORK_MODE, RILConstants.NETWORK_MODE_GSM_ONLY); break; case CM_MODE_3G: intent.putExtra(EXTRA_NETWORK_MODE, RILConstants.NETWORK_MODE_WCDMA_ONLY); break; case CM_MODE_4G: intent.putExtra(EXTRA_NETWORK_MODE, RILConstants.NETWORK_MODE_LTE_ONLY); break; case CM_MODE_2G3G: intent.putExtra(EXTRA_NETWORK_MODE, RILConstants.NETWORK_MODE_WCDMA_PREF); break; case CM_MODE_ALL: intent.putExtra(EXTRA_NETWORK_MODE, RILConstants.NETWORK_MODE_LTE_GSM_WCDMA); break; default: return; } context.sendBroadcast(intent); } break; case PROFILE_CONNECTION_BLUETOOTH: int btstate = bta.getState(); if (forcedState && (btstate == BluetoothAdapter.STATE_OFF || btstate == BluetoothAdapter.STATE_TURNING_OFF)) { bta.enable(); } else if (!forcedState && (btstate == BluetoothAdapter.STATE_ON || btstate == BluetoothAdapter.STATE_TURNING_ON)) { bta.disable(); } break; case PROFILE_CONNECTION_GPS: currentState = lm.isProviderEnabled(LocationManager.GPS_PROVIDER); if (currentState != forcedState) { Settings.Secure.setLocationProviderEnabled(context.getContentResolver(), LocationManager.GPS_PROVIDER, forcedState); } break; case PROFILE_CONNECTION_SYNC: currentState = ContentResolver.getMasterSyncAutomatically(); if (forcedState != currentState) { ContentResolver.setMasterSyncAutomatically(forcedState); } break; case PROFILE_CONNECTION_WIFI: int wifiApState = wm.getWifiApState(); currentState = wm.isWifiEnabled(); if (currentState != forcedState) { // Disable wifi tether if (forcedState && (wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) || (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED)) { wm.setWifiApEnabled(null, false); } wm.setWifiEnabled(forcedState); } break; case PROFILE_CONNECTION_WIFIAP: int wifiState = wm.getWifiState(); currentState = wm.isWifiApEnabled(); if (currentState != forcedState) { // Disable wifi if (forcedState && (wifiState == WifiManager.WIFI_STATE_ENABLING) || (wifiState == WifiManager.WIFI_STATE_ENABLED)) { wm.setWifiEnabled(false); } wm.setWifiApEnabled(null, forcedState); } break; case PROFILE_CONNECTION_WIMAX: if (WimaxHelper.isWimaxSupported(context)) { currentState = WimaxHelper.isWimaxEnabled(context); if (currentState != forcedState) { WimaxHelper.setWimaxEnabled(context, forcedState); } } break; case PROFILE_CONNECTION_NFC: if (nfcAdapter != null) { int adapterState = nfcAdapter.getAdapterState(); currentState = (adapterState == NfcAdapter.STATE_ON || adapterState == NfcAdapter.STATE_TURNING_ON); if (currentState != forcedState) { if (forcedState) { nfcAdapter.enable(); } else if (!forcedState && adapterState != NfcAdapter.STATE_TURNING_OFF) { nfcAdapter.disable(); } } } break; } } /** @hide */ public static ConnectionSettings fromXml(XmlPullParser xpp, Context context) throws XmlPullParserException, IOException { int event = xpp.next(); ConnectionSettings connectionDescriptor = new ConnectionSettings(0); while (event != XmlPullParser.END_TAG || !xpp.getName().equals("connectionDescriptor")) { if (event == XmlPullParser.START_TAG) { String name = xpp.getName(); if (name.equals("connectionId")) { connectionDescriptor.mConnectionId = Integer.parseInt(xpp.nextText()); } else if (name.equals("value")) { connectionDescriptor.mValue = Integer.parseInt(xpp.nextText()); } else if (name.equals("override")) { connectionDescriptor.mOverride = Boolean.parseBoolean(xpp.nextText()); } else if (name.equals("subId")) { connectionDescriptor.mSubId = Integer.parseInt(xpp.nextText()); } } else if (event == XmlPullParser.END_DOCUMENT) { throw new IOException("Premature end of file while parsing connection settings"); } event = xpp.next(); } return connectionDescriptor; } /** @hide */ public void getXmlString(StringBuilder builder, Context context) { builder.append("\n"); builder.append(mConnectionId); builder.append("\n"); builder.append(mValue); builder.append("\n"); builder.append(mOverride); builder.append("\n"); if (Build.CM_VERSION.SDK_INT >= Build.CM_VERSION_CODES.ELDERBERRY) { if (mConnectionId == PROFILE_CONNECTION_2G3G4G && mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { builder.append("").append(mSubId).append("\n"); } } builder.append("\n"); } @Override public int describeContents() { return 0; } /** @hide */ @Override public void writeToParcel(Parcel dest, int flags) { // Tell the concierge to prepare the parcel ParcelInfo parcelInfo = Concierge.prepareParcel(dest); // === BOYSENBERRY === dest.writeInt(mConnectionId); dest.writeInt(mOverride ? 1 : 0); dest.writeInt(mValue); dest.writeInt(mDirty ? 1 : 0); // === ELDERBERRY === if (mConnectionId == PROFILE_CONNECTION_2G3G4G) { dest.writeInt(mSubId); } // Complete the parcel info for the concierge parcelInfo.complete(); } /** @hide */ public void readFromParcel(Parcel in) { // Read parcelable version via the Concierge ParcelInfo parcelInfo = Concierge.receiveParcel(in); int parcelableVersion = parcelInfo.getParcelVersion(); // Pattern here is that all new members should be added to the end of // the writeToParcel method. Then we step through each version, until the latest // API release to help unravel this parcel if (parcelableVersion >= Build.CM_VERSION_CODES.BOYSENBERRY) { mConnectionId = in.readInt(); mOverride = in.readInt() != 0; mValue = in.readInt(); mDirty = in.readInt() != 0; } if (parcelableVersion >= Build.CM_VERSION_CODES.ELDERBERRY) { if (mConnectionId == PROFILE_CONNECTION_2G3G4G) { mSubId = in.readInt(); } } // Complete parcel info for the concierge parcelInfo.complete(); } }