/* * 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.media.AudioManager; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; import com.android.internal.policy.IKeyguardService; import cyanogenmod.os.Build; import cyanogenmod.profiles.AirplaneModeSettings; import cyanogenmod.profiles.BrightnessSettings; import cyanogenmod.profiles.ConnectionSettings; import cyanogenmod.profiles.LockSettings; import cyanogenmod.profiles.RingModeSettings; import cyanogenmod.profiles.StreamSettings; import cyanogenmod.os.Concierge; import cyanogenmod.os.Concierge.ParcelInfo; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.UUID; /** * A class that represents a device profile. * * A {@link Profile} can serve a multitude of purposes, allowing the creator(user) * to set overrides for streams, triggers, screen lock, brightness, various other * settings. */ public final class Profile implements Parcelable, Comparable { private String mName; private int mNameResId; private UUID mUuid; private ArrayList mSecondaryUuids = new ArrayList(); private Map profileGroups = new HashMap(); private ProfileGroup mDefaultGroup; private boolean mStatusBarIndicator = false; private boolean mDirty; private static final String TAG = "Profile"; private int mProfileType; private Map streams = new HashMap(); private Map mTriggers = new HashMap(); private Map connections = new HashMap(); private Map networkConnectionSubIds = new HashMap<>(); private RingModeSettings mRingMode = new RingModeSettings(); private AirplaneModeSettings mAirplaneMode = new AirplaneModeSettings(); private BrightnessSettings mBrightness = new BrightnessSettings(); private LockSettings mScreenLockMode = new LockSettings(); private int mExpandedDesktopMode = ExpandedDesktopMode.DEFAULT; private int mDozeMode = DozeMode.DEFAULT; private int mNotificationLightMode = NotificationLightMode.DEFAULT; /** * Lock modes of a device */ public static class LockMode { /** Represents a default state lock mode (user choice) */ public static final int DEFAULT = 0; /** Represents an insecure state lock mode, where the device has no security screen */ public static final int INSECURE = 1; /** Represents a disabled state lock mode, where the devices lock screen can be removed */ public static final int DISABLE = 2; } /** * Expanded desktop modes available on a device */ public static class ExpandedDesktopMode { /** Represents a default state expanded desktop mode (user choice) */ public static final int DEFAULT = 0; /** Represents an enabled expanded desktop mode */ public static final int ENABLE = 1; /** Represents a disabled expanded desktop mode */ public static final int DISABLE = 2; } /** * Doze modes available on a device */ public static class DozeMode { /** Represents a default Doze mode (user choice) */ public static final int DEFAULT = 0; /** Represents an enabled Doze mode */ public static final int ENABLE = 1; /** Represents an disabled Doze mode */ public static final int DISABLE = 2; } /** * Notification light modes available on a device */ public static class NotificationLightMode { /** Represents a default Notification light mode (user choice) */ public static final int DEFAULT = 0; /** Represents an enabled Notification light mode */ public static final int ENABLE = 1; /** Represents a disabled Notification light mode */ public static final int DISABLE = 2; } /** * Available trigger types on the device, usually hardware */ public static class TriggerType { /** Represents a WiFi trigger type */ public static final int WIFI = 0; /** Represents a Bluetooth trigger type */ public static final int BLUETOOTH = 1; } /** * Various trigger states associated with a {@link TriggerType} */ public static class TriggerState { /** A {@link TriggerState) for when the {@link TriggerType} connects */ public static final int ON_CONNECT = 0; /** A {@link TriggerState) for when the {@link TriggerType} disconnects */ public static final int ON_DISCONNECT = 1; /** A {@link TriggerState) for when the {@link TriggerType} is disabled */ public static final int DISABLED = 2; /** * A {@link TriggerState) for when the {@link TriggerType#BLUETOOTH} * connects for A2DP session */ public static final int ON_A2DP_CONNECT = 3; /** * A {@link TriggerState) for when the {@link TriggerType#BLUETOOTH} * disconnects from A2DP session */ public static final int ON_A2DP_DISCONNECT = 4; } /** * A {@link Profile} type */ public static class Type { /** Profile type which represents a toggle {@link Profile} */ public static final int TOGGLE = 0; /** Profile type which represents a conditional {@link Profile} */ public static final int CONDITIONAL = 1; } /** * A {@link ProfileTrigger} is a {@link TriggerType} which can be queried from the OS */ public static class ProfileTrigger implements Parcelable { private int mType; private String mId; private String mName; private int mState; /** * Construct a {@link ProfileTrigger} based on its type {@link Profile.TriggerType} and if * the trigger should fire on a {@link Profile.TriggerState} change. * * Example: *
         *   triggerId = trigger.getSSID();                  // Use the AP's SSID as identifier
         *   triggerName = trigger.getTitle();               // Use the AP's name as the trigger name
         *   triggerType = Profile.TriggerType.WIFI;         // This is a wifi trigger
         *   triggerState = Profile.TriggerState.ON_CONNECT; // On Connect of this, trigger
         *
         *   Profile.ProfileTrigger profileTrigger =
         *           new Profile.ProfileTrigger(triggerType, triggerId, triggerState, triggerName);
         * 
* * @param type a {@link Profile.TriggerType} * @param id an identifier for the ProfileTrigger * @param state {@link Profile.TriggerState} depending on the TriggerType * @param name an identifying name for the ProfileTrigger */ public ProfileTrigger(int type, String id, int state, String name) { mType = type; mId = id; mState = state; mName = name; } private ProfileTrigger(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) { mType = in.readInt(); mId = in.readString(); mState = in.readInt(); mName = in.readString(); } // Complete parcel info for the concierge parcelInfo.complete(); } @Override public void writeToParcel(Parcel dest, int flags) { // Tell the concierge to prepare the parcel ParcelInfo parcelInfo = Concierge.prepareParcel(dest); dest.writeInt(mType); dest.writeString(mId); dest.writeInt(mState); dest.writeString(mName); // Complete the parcel info for the concierge parcelInfo.complete(); } @Override public int describeContents() { return 0; } /** * Get the {@link ProfileTrigger} {@link TriggerType} * @return {@link TriggerType} */ public int getType() { return mType; } /** * Get the name associated with the {@link ProfileTrigger} * @return a string name */ public String getName() { return mName; } /** * Get the id associated with the {@link ProfileTrigger} * @return an string identifier */ public String getId() { return mId; } /** * Get the state associated with the {@link ProfileTrigger} * @return an integer indicating the state */ public int getState() { return mState; } /** * @hide */ public void getXmlString(StringBuilder builder, Context context) { final String itemType = mType == TriggerType.WIFI ? "wifiAP" : "btDevice"; builder.append("<"); builder.append(itemType); builder.append(" "); builder.append(getIdType(mType)); builder.append("=\""); builder.append(mId); builder.append("\" state=\""); builder.append(mState); builder.append("\" name=\""); builder.append(mName); builder.append("\">\n"); } /** * @hide */ public static ProfileTrigger fromXml(XmlPullParser xpp, Context context) { final String name = xpp.getName(); final int type; if (name.equals("wifiAP")) { type = TriggerType.WIFI; } else if (name.equals("btDevice")) { type = TriggerType.BLUETOOTH; } else { return null; } String id = xpp.getAttributeValue(null, getIdType(type)); int state = Integer.valueOf(xpp.getAttributeValue(null, "state")); String triggerName = xpp.getAttributeValue(null, "name"); if (triggerName == null) { triggerName = id; } return new ProfileTrigger(type, id, state, triggerName); } private static String getIdType(int type) { return type == TriggerType.WIFI ? "ssid" : "address"; } /** * @hide */ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public ProfileTrigger createFromParcel(Parcel in) { return new ProfileTrigger(in); } @Override public ProfileTrigger[] newArray(int size) { return new ProfileTrigger[size]; } }; } /** @hide */ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public Profile createFromParcel(Parcel in) { return new Profile(in); } @Override public Profile[] newArray(int size) { return new Profile[size]; } }; public Profile(String name) { this(name, -1, UUID.randomUUID()); } /** @hide */ public Profile(String name, int nameResId, UUID uuid) { mName = name; mNameResId = nameResId; mUuid = uuid; mProfileType = Type.TOGGLE; //Default to toggle type mDirty = false; } private Profile(Parcel in) { readFromParcel(in); } /** * Get the {@link TriggerState} for a {@link ProfileTrigger} with a given id * @param type {@link TriggerType} * @param id string id of {@link ProfileTrigger} * @return {@link TriggerState} */ public int getTriggerState(int type, String id) { ProfileTrigger trigger = id != null ? mTriggers.get(id) : null; if (trigger != null) { return trigger.mState; } return TriggerState.DISABLED; } /** * Get all the {@link ProfileTrigger}s for a given {@link TriggerType} * @param type {@link TriggerType} * @return an array list of {@link ProfileTrigger}s */ public ArrayList getTriggersFromType(int type) { ArrayList result = new ArrayList(); for (Entry profileTrigger: mTriggers.entrySet()) { ProfileTrigger trigger = profileTrigger.getValue(); if (trigger.getType() == type) { result.add(trigger); } } return result; } /** * Set a custom {@link ProfileTrigger} * @hide */ public void setTrigger(int type, String id, int state, String name) { if (id == null || type < TriggerType.WIFI || type > TriggerType.BLUETOOTH || state < TriggerState.ON_CONNECT || state > TriggerState.ON_A2DP_DISCONNECT) { return; } ProfileTrigger trigger = mTriggers.get(id); if (state == TriggerState.DISABLED) { if (trigger != null) { mTriggers.remove(id); } } else if (trigger != null) { trigger.mState = state; } else { mTriggers.put(id, new ProfileTrigger(type, id, state, name)); } mDirty = true; } /** * Set a {@link ProfileTrigger} on the {@link Profile} * @param trigger a {@link ProfileTrigger} */ public void setTrigger(ProfileTrigger trigger) { setTrigger(trigger.getType(), trigger.getId(), trigger.getState(), trigger.getName()); } public int compareTo(Object obj) { Profile tmp = (Profile) obj; if (mName.compareTo(tmp.mName) < 0) { return -1; } else if (mName.compareTo(tmp.mName) > 0) { return 1; } return 0; } /** * Add a {@link ProfileGroup} to the {@link Profile} * @param profileGroup * @hide */ public void addProfileGroup(ProfileGroup profileGroup) { if (profileGroup == null) { return; } if (profileGroup.isDefaultGroup()) { /* we must not have more than one default group */ if (mDefaultGroup != null) { return; } mDefaultGroup = profileGroup; } profileGroups.put(profileGroup.getUuid(), profileGroup); mDirty = true; } /** * Remove a {@link ProfileGroup} with a given {@link UUID} * @param uuid * @hide */ public void removeProfileGroup(UUID uuid) { if (!profileGroups.get(uuid).isDefaultGroup()) { profileGroups.remove(uuid); } else { Log.e(TAG, "Cannot remove default group: " + uuid); } } /** * Get {@link ProfileGroup}s associated with the {@link Profile} * @return {@link ProfileGroup[]} * @hide */ public ProfileGroup[] getProfileGroups() { return profileGroups.values().toArray(new ProfileGroup[profileGroups.size()]); } /** * Get a {@link ProfileGroup} with a given {@link UUID} * @param uuid * @return a {@link ProfileGroup} * @hide */ public ProfileGroup getProfileGroup(UUID uuid) { return profileGroups.get(uuid); } /** * Get the default {@link ProfileGroup} associated with the {@link Profile} * @return the default {@link ProfileGroup} * @hide */ public ProfileGroup getDefaultGroup() { return mDefaultGroup; } /** @hide */ @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 === if (!TextUtils.isEmpty(mName)) { dest.writeInt(1); dest.writeString(mName); } else { dest.writeInt(0); } if (mNameResId != 0) { dest.writeInt(1); dest.writeInt(mNameResId); } else { dest.writeInt(0); } if (mUuid != null) { dest.writeInt(1); new ParcelUuid(mUuid).writeToParcel(dest, 0); } else { dest.writeInt(0); } if (mSecondaryUuids != null && !mSecondaryUuids.isEmpty()) { ArrayList uuids = new ArrayList(mSecondaryUuids.size()); for (UUID u : mSecondaryUuids) { uuids.add(new ParcelUuid(u)); } dest.writeInt(1); dest.writeParcelableArray(uuids.toArray(new Parcelable[uuids.size()]), flags); } else { dest.writeInt(0); } dest.writeInt(mStatusBarIndicator ? 1 : 0); dest.writeInt(mProfileType); dest.writeInt(mDirty ? 1 : 0); if (profileGroups != null && !profileGroups.isEmpty()) { dest.writeInt(1); dest.writeTypedArray(profileGroups.values().toArray( new ProfileGroup[0]), flags); } else { dest.writeInt(0); } if (streams != null && !streams.isEmpty()) { dest.writeInt(1); dest.writeTypedArray(streams.values().toArray( new StreamSettings[0]), flags); } else { dest.writeInt(0); } if (connections != null && !connections.isEmpty()) { dest.writeInt(1); dest.writeTypedArray(connections.values().toArray( new ConnectionSettings[0]), flags); } else { dest.writeInt(0); } if (mRingMode != null) { dest.writeInt(1); mRingMode.writeToParcel(dest, 0); } else { dest.writeInt(0); } if (mAirplaneMode != null) { dest.writeInt(1); mAirplaneMode.writeToParcel(dest, 0); } else { dest.writeInt(0); } if (mBrightness != null) { dest.writeInt(1); mBrightness.writeToParcel(dest, 0); } else { dest.writeInt(0); } if (mScreenLockMode != null) { dest.writeInt(1); mScreenLockMode.writeToParcel(dest, 0); } else { dest.writeInt(0); } dest.writeTypedArray(mTriggers.values().toArray(new ProfileTrigger[0]), flags); dest.writeInt(mExpandedDesktopMode); dest.writeInt(mDozeMode); // === ELDERBERRY === dest.writeInt(mNotificationLightMode); if (networkConnectionSubIds != null && !networkConnectionSubIds.isEmpty()) { dest.writeInt(1); dest.writeTypedArray(networkConnectionSubIds.values().toArray( new ConnectionSettings[0]), flags); } else { dest.writeInt(0); } // 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) { if (in.readInt() != 0) { mName = in.readString(); } if (in.readInt() != 0) { mNameResId = in.readInt(); } if (in.readInt() != 0) { mUuid = ParcelUuid.CREATOR.createFromParcel(in).getUuid(); } if (in.readInt() != 0) { for (Parcelable parcel : in.readParcelableArray(null)) { ParcelUuid u = (ParcelUuid) parcel; mSecondaryUuids.add(u.getUuid()); } } mStatusBarIndicator = (in.readInt() == 1); mProfileType = in.readInt(); mDirty = (in.readInt() == 1); if (in.readInt() != 0) { for (ProfileGroup group : in.createTypedArray(ProfileGroup.CREATOR)) { profileGroups.put(group.getUuid(), group); if (group.isDefaultGroup()) { mDefaultGroup = group; } } } if (in.readInt() != 0) { for (StreamSettings stream : in.createTypedArray(StreamSettings.CREATOR)) { streams.put(stream.getStreamId(), stream); } } if (in.readInt() != 0) { for (ConnectionSettings connection : in.createTypedArray(ConnectionSettings.CREATOR)) { connections.put(connection.getConnectionId(), connection); } } if (in.readInt() != 0) { mRingMode = RingModeSettings.CREATOR.createFromParcel(in); } if (in.readInt() != 0) { mAirplaneMode = AirplaneModeSettings.CREATOR.createFromParcel(in); } if (in.readInt() != 0) { mBrightness = BrightnessSettings.CREATOR.createFromParcel(in); } if (in.readInt() != 0) { mScreenLockMode = LockSettings.CREATOR.createFromParcel(in); } for (ProfileTrigger trigger : in.createTypedArray(ProfileTrigger.CREATOR)) { mTriggers.put(trigger.mId, trigger); } mExpandedDesktopMode = in.readInt(); mDozeMode = in.readInt(); } if (parcelableVersion >= Build.CM_VERSION_CODES.ELDERBERRY) { mNotificationLightMode = in.readInt(); if (in.readInt() != 0) { for (ConnectionSettings connection : in.createTypedArray(ConnectionSettings.CREATOR)) { // elderberry can do msim connections networkConnectionSubIds.put(connection.getSubId(), connection); } } } // Complete the parcel info for the concierge parcelInfo.complete(); } /** * Get the name associated with the {@link Profile} * @return a string name of the profile */ public String getName() { return mName; } /** * Set a name for the {@link Profile} * @param name a string for the {@link Profile} */ public void setName(String name) { mName = name; mNameResId = -1; mDirty = true; } /** * Get the {@link Type} of the {@link Profile} * @return */ public int getProfileType() { return mProfileType; } /** * Set the {@link Type} for the {@link Profile} * @param type a type of profile */ public void setProfileType(int type) { mProfileType = type; mDirty = true; } /** * Get the {@link UUID} associated with the {@link Profile} * @return the uuid for the profile */ public UUID getUuid() { if (this.mUuid == null) this.mUuid = UUID.randomUUID(); return this.mUuid; } /** * Get the secondary {@link UUID}s for the {@link Profile} * @return the secondary uuids for the Profile */ public UUID[] getSecondaryUuids() { return mSecondaryUuids.toArray(new UUID[mSecondaryUuids.size()]); } /** * Set a list of secondary {@link UUID}s for the {@link Profile} * @param uuids */ public void setSecondaryUuids(List uuids) { mSecondaryUuids.clear(); if (uuids != null) { mSecondaryUuids.addAll(uuids); mDirty = true; } } /** * Add a secondary {@link UUID} to the {@link Profile} * @param uuid */ public void addSecondaryUuid(UUID uuid) { if (uuid != null) { mSecondaryUuids.add(uuid); mDirty = true; } } /** * @hide */ public boolean getStatusBarIndicator() { return mStatusBarIndicator; } /** * @hide */ public void setStatusBarIndicator(boolean newStatusBarIndicator) { mStatusBarIndicator = newStatusBarIndicator; mDirty = true; } /** * Check if the given {@link Profile} is a {@link Type#CONDITIONAL} * @return true if conditional */ public boolean isConditionalType() { return(mProfileType == Type.CONDITIONAL ? true : false); } /** * @hide */ public void setConditionalType() { mProfileType = Type.CONDITIONAL; mDirty = true; } /** * Get the {@link RingModeSettings} for the {@link Profile} * @return */ public RingModeSettings getRingMode() { return mRingMode; } /** * Set the {@link RingModeSettings} for the {@link Profile} * @param descriptor */ public void setRingMode(RingModeSettings descriptor) { mRingMode = descriptor; mDirty = true; } /** * Get the {@link LockSettings} for the {@link Profile} * @return */ public LockSettings getScreenLockMode() { return mScreenLockMode; } /** * Set the {@link LockSettings} for the {@link Profile} * @param screenLockMode */ public void setScreenLockMode(LockSettings screenLockMode) { mScreenLockMode = screenLockMode; mDirty = true; } /** * Get the {@link ExpandedDesktopMode} for the {@link Profile} * @return */ public int getExpandedDesktopMode() { return mExpandedDesktopMode; } /** * Set the {@link ExpandedDesktopMode} for the {@link Profile} * @return */ public void setExpandedDesktopMode(int expandedDesktopMode) { if (expandedDesktopMode < ExpandedDesktopMode.DEFAULT || expandedDesktopMode > ExpandedDesktopMode.DISABLE) { mExpandedDesktopMode = ExpandedDesktopMode.DEFAULT; } else { mExpandedDesktopMode = expandedDesktopMode; } mDirty = true; } /** * Get the {@link DozeMode} associated with the {@link Profile} * @return */ public int getDozeMode() { return mDozeMode; } /** * Set the {@link DozeMode} associated with the {@link Profile} * @return */ public void setDozeMode(int dozeMode) { if (dozeMode < DozeMode.DEFAULT || dozeMode > DozeMode.DISABLE) { mDozeMode = DozeMode.DEFAULT; } else { mDozeMode = dozeMode; } mDirty = true; } /** * Get the {@link NotificationLightMode} associated with the {@link Profile} * @return */ public int getNotificationLightMode() { return mNotificationLightMode; } /** * Set the {@link NotificationLightMode} associated with the {@link Profile} * @return */ public void setNotificationLightMode(int notificationLightMode) { if (notificationLightMode < NotificationLightMode.DEFAULT || notificationLightMode > NotificationLightMode.DISABLE) { mNotificationLightMode = NotificationLightMode.DEFAULT; } else { mNotificationLightMode = notificationLightMode; } mDirty = true; } /** * Get the {@link AirplaneModeSettings} associated with the {@link Profile} * @return */ public AirplaneModeSettings getAirplaneMode() { return mAirplaneMode; } /** * Set the {@link AirplaneModeSettings} associated with the {@link Profile} * @param descriptor */ public void setAirplaneMode(AirplaneModeSettings descriptor) { mAirplaneMode = descriptor; mDirty = true; } /** * Get the {@link BrightnessSettings} associated with the {@link Profile} * @return */ public BrightnessSettings getBrightness() { return mBrightness; } /** * Set the {@link BrightnessSettings} associated with the {@link Profile} * @return */ public void setBrightness(BrightnessSettings descriptor) { mBrightness = descriptor; mDirty = true; } /** @hide */ public boolean isDirty() { if (mDirty) { return true; } for (ProfileGroup group : profileGroups.values()) { if (group.isDirty()) { return true; } } for (StreamSettings stream : streams.values()) { if (stream.isDirty()) { return true; } } for (ConnectionSettings conn : connections.values()) { if (conn.isDirty()) { return true; } } for (ConnectionSettings conn : networkConnectionSubIds.values()) { if (conn.isDirty()) { return true; } } if (mRingMode.isDirty()) { return true; } if (mAirplaneMode.isDirty()) { return true; } if (mBrightness.isDirty()) { return true; } return false; } /** @hide */ public void getXmlString(StringBuilder builder, Context context) { builder.append(" 0) { builder.append("nameres=\""); builder.append(context.getResources().getResourceEntryName(mNameResId)); } else { builder.append("name=\""); builder.append(TextUtils.htmlEncode(getName())); } builder.append("\" uuid=\""); builder.append(TextUtils.htmlEncode(getUuid().toString())); builder.append("\">\n"); builder.append(""); for (UUID u : mSecondaryUuids) { builder.append(""); builder.append(TextUtils.htmlEncode(u.toString())); builder.append(""); } builder.append("\n"); builder.append(""); builder.append(getProfileType() == Type.TOGGLE ? "toggle" : "conditional"); builder.append("\n"); builder.append(""); builder.append(getStatusBarIndicator() ? "yes" : "no"); builder.append("\n"); if (mScreenLockMode != null) { builder.append(""); mScreenLockMode.writeXmlString(builder, context); builder.append("\n"); } builder.append(""); builder.append(mExpandedDesktopMode); builder.append("\n"); builder.append(""); builder.append(mDozeMode); builder.append("\n"); builder.append(""); builder.append(mNotificationLightMode); builder.append("\n"); mAirplaneMode.getXmlString(builder, context); mBrightness.getXmlString(builder, context); mRingMode.getXmlString(builder, context); for (ProfileGroup pGroup : profileGroups.values()) { pGroup.getXmlString(builder, context); } for (StreamSettings sd : streams.values()) { sd.getXmlString(builder, context); } for (ConnectionSettings cs : connections.values()) { cs.getXmlString(builder, context); } for (ConnectionSettings cs : networkConnectionSubIds.values()) { cs.getXmlString(builder, context); } if (!mTriggers.isEmpty()) { builder.append("\n"); for (ProfileTrigger trigger : mTriggers.values()) { trigger.getXmlString(builder, context); } builder.append("\n"); } builder.append("\n"); mDirty = false; } private static List readSecondaryUuidsFromXml(XmlPullParser xpp, Context context) throws XmlPullParserException, IOException { ArrayList uuids = new ArrayList(); int event = xpp.next(); while (event != XmlPullParser.END_TAG || !xpp.getName().equals("uuids")) { if (event == XmlPullParser.START_TAG) { String name = xpp.getName(); if (name.equals("uuid")) { try { uuids.add(UUID.fromString(xpp.nextText())); } catch (NullPointerException e) { Log.w(TAG, "Null Pointer - invalid UUID"); } catch (IllegalArgumentException e) { Log.w(TAG, "UUID not recognized"); } } } event = xpp.next(); } return uuids; } private static void readTriggersFromXml(XmlPullParser xpp, Context context, Profile profile) throws XmlPullParserException, IOException { int event = xpp.next(); while (event != XmlPullParser.END_TAG || !xpp.getName().equals("triggers")) { if (event == XmlPullParser.START_TAG) { ProfileTrigger trigger = ProfileTrigger.fromXml(xpp, context); if (trigger != null) { profile.mTriggers.put(trigger.mId, trigger); } } else if (event == XmlPullParser.END_DOCUMENT) { throw new IOException("Premature end of file while parsing triggers"); } event = xpp.next(); } } /** @hide */ public void validateRingtones(Context context) { for (ProfileGroup pg : profileGroups.values()) { pg.validateOverrideUris(context); } } /** @hide */ public static Profile fromXml(XmlPullParser xpp, Context context) throws XmlPullParserException, IOException { String value = xpp.getAttributeValue(null, "nameres"); int profileNameResId = -1; String profileName = null; if (value != null) { profileNameResId = context.getResources().getIdentifier(value, "string", "cyanogenmod.platform"); if (profileNameResId > 0) { profileName = context.getResources().getString(profileNameResId); } } if (profileName == null) { profileName = xpp.getAttributeValue(null, "name"); } UUID profileUuid = UUID.randomUUID(); try { profileUuid = UUID.fromString(xpp.getAttributeValue(null, "uuid")); } catch (NullPointerException e) { Log.w(TAG, "Null Pointer - UUID not found for " + profileName + ". New UUID generated: " + profileUuid.toString() ); } catch (IllegalArgumentException e) { Log.w(TAG, "UUID not recognized for " + profileName + ". New UUID generated: " + profileUuid.toString() ); } Profile profile = new Profile(profileName, profileNameResId, profileUuid); int event = xpp.next(); while (event != XmlPullParser.END_TAG) { if (event == XmlPullParser.START_TAG) { String name = xpp.getName(); if (name.equals("uuids")) { profile.setSecondaryUuids(readSecondaryUuidsFromXml(xpp, context)); } if (name.equals("statusbar")) { profile.setStatusBarIndicator(xpp.nextText().equals("yes")); } if (name.equals("profiletype")) { profile.setProfileType(xpp.nextText().equals("toggle") ? Type.TOGGLE : Type.CONDITIONAL); } if (name.equals("ringModeDescriptor")) { RingModeSettings smd = RingModeSettings.fromXml(xpp, context); profile.setRingMode(smd); } if (name.equals("airplaneModeDescriptor")) { AirplaneModeSettings amd = AirplaneModeSettings.fromXml(xpp, context); profile.setAirplaneMode(amd); } if (name.equals("brightnessDescriptor")) { BrightnessSettings bd = BrightnessSettings.fromXml(xpp, context); profile.setBrightness(bd); } if (name.equals("screen-lock-mode")) { LockSettings lockMode = new LockSettings(Integer.valueOf(xpp.nextText())); profile.setScreenLockMode(lockMode); } if (name.equals("expanded-desktop-mode")) { profile.setExpandedDesktopMode(Integer.valueOf(xpp.nextText())); } if (name.equals("doze-mode")) { profile.setDozeMode(Integer.valueOf(xpp.nextText())); } if (name.equals("notification-light-mode")) { profile.setNotificationLightMode(Integer.valueOf(xpp.nextText())); } if (name.equals("profileGroup")) { ProfileGroup pg = ProfileGroup.fromXml(xpp, context); profile.addProfileGroup(pg); } if (name.equals("streamDescriptor")) { StreamSettings sd = StreamSettings.fromXml(xpp, context); profile.setStreamSettings(sd); } if (name.equals("connectionDescriptor")) { ConnectionSettings cs = ConnectionSettings.fromXml(xpp, context); if (Build.CM_VERSION.SDK_INT >= Build.CM_VERSION_CODES.ELDERBERRY && cs.getConnectionId() == ConnectionSettings.PROFILE_CONNECTION_2G3G4G) { profile.networkConnectionSubIds.put(cs.getSubId(), cs); } else { profile.connections.put(cs.getConnectionId(), cs); } } if (name.equals("triggers")) { readTriggersFromXml(xpp, context, profile); } } else if (event == XmlPullParser.END_DOCUMENT) { throw new IOException("Premature end of file while parsing profle:" + profileName); } event = xpp.next(); } /* we just loaded from XML, so nothing needs saving */ profile.mDirty = false; return profile; } /** @hide */ public void doSelect(Context context, IKeyguardService keyguardService) { // Set stream volumes AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); for (StreamSettings sd : streams.values()) { if (sd.isOverride()) { am.setStreamVolume(sd.getStreamId(), sd.getValue(), 0); } } // Set connections for (ConnectionSettings cs : connections.values()) { if (cs.isOverride()) { cs.processOverride(context); } } for (ConnectionSettings cs : networkConnectionSubIds.values()) { if (cs.isOverride()) { cs.processOverride(context); } } // Set ring mode mRingMode.processOverride(context); // Set airplane mode mAirplaneMode.processOverride(context); // Set brightness mBrightness.processOverride(context); if (keyguardService != null) { // Set lock screen mode mScreenLockMode.processOverride(context, keyguardService); } else { Log.e(TAG, "cannot process screen lock override without a keyguard service."); } // Set expanded desktop // if (mExpandedDesktopMode != ExpandedDesktopMode.DEFAULT) { // Settings.System.putIntForUser(context.getContentResolver(), // Settings.System.EXPANDED_DESKTOP_STATE, // mExpandedDesktopMode == ExpandedDesktopMode.ENABLE ? 1 : 0, // UserHandle.USER_CURRENT); // } // Set doze mode if (mDozeMode != DozeMode.DEFAULT) { Settings.Secure.putIntForUser(context.getContentResolver(), Settings.Secure.DOZE_ENABLED, mDozeMode == DozeMode.ENABLE ? 1 : 0, UserHandle.USER_CURRENT); } // Set notification light mode if (mNotificationLightMode != NotificationLightMode.DEFAULT) { Settings.System.putIntForUser(context.getContentResolver(), Settings.System.NOTIFICATION_LIGHT_PULSE, mNotificationLightMode == NotificationLightMode.ENABLE ? 1 : 0, UserHandle.USER_CURRENT); } } /** * Get the settings for a stream id in the {@link Profile} * @return {@link StreamSettings} */ public StreamSettings getSettingsForStream(int streamId){ return streams.get(streamId); } /** * Set the {@link StreamSettings} for the {@link Profile} * @param descriptor */ public void setStreamSettings(StreamSettings descriptor){ streams.put(descriptor.getStreamId(), descriptor); mDirty = true; } /** * Get the {@link StreamSettings} for the {@link Profile} * @return {@link Collection} */ public Collection getStreamSettings(){ return streams.values(); } /** * Get the settings for a connection id in the {@link Profile} * @return {@link ConnectionSettings} */ public ConnectionSettings getSettingsForConnection(int connectionId){ if (connectionId == ConnectionSettings.PROFILE_CONNECTION_2G3G4G) { if (networkConnectionSubIds.size() > 1) { throw new UnsupportedOperationException("Use getConnectionSettingsWithSubId for MSIM devices!"); } else { return networkConnectionSubIds.values().iterator().next(); } } return connections.get(connectionId); } /** * Get the settings for a {@link ConnectionSettings#PROFILE_CONNECTION_2G3G4G} by sub id. * * @param subId the sub id to lookup. Can be {@link android.telephony.SubscriptionManager#INVALID_SUBSCRIPTION_ID} * @return {@link ConnectionSettings} */ public ConnectionSettings getConnectionSettingWithSubId(int subId) { return networkConnectionSubIds.get(subId); } /** * Set the {@link ConnectionSettings} for the {@link Profile} * @param descriptor */ public void setConnectionSettings(ConnectionSettings descriptor) { if (descriptor.getConnectionId() == ConnectionSettings.PROFILE_CONNECTION_2G3G4G) { networkConnectionSubIds.put(descriptor.getSubId(), descriptor); } else { connections.put(descriptor.getConnectionId(), descriptor); } mDirty = true; } /** * Get the {@link ConnectionSettings} for the {@link Profile} * @return {@link Collection} */ public Collection getConnectionSettings(){ List combinedList = new ArrayList<>(); combinedList.addAll(connections.values()); combinedList.addAll(networkConnectionSubIds.values()); return combinedList; } }