summaryrefslogtreecommitdiffstats
path: root/services/core/java/com/android/server
diff options
context:
space:
mode:
Diffstat (limited to 'services/core/java/com/android/server')
-rw-r--r--services/core/java/com/android/server/AppOpsPolicy.java441
-rw-r--r--services/core/java/com/android/server/AppOpsService.java480
-rw-r--r--services/core/java/com/android/server/AssetAtlasService.java5
-rw-r--r--services/core/java/com/android/server/BasePermissionDialog.java84
-rw-r--r--services/core/java/com/android/server/BatteryService.java170
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java13
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java9
-rw-r--r--services/core/java/com/android/server/InputMethodManagerService.java19
-rw-r--r--services/core/java/com/android/server/LocationManagerService.java70
-rw-r--r--services/core/java/com/android/server/MasterClearReceiver.java6
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java2
-rw-r--r--services/core/java/com/android/server/PermissionDialog.java141
-rw-r--r--services/core/java/com/android/server/PermissionDialogReqQueue.java82
-rw-r--r--services/core/java/com/android/server/SystemConfig.java45
-rw-r--r--services/core/java/com/android/server/ThemeService.java1244
-rw-r--r--services/core/java/com/android/server/WiredAccessoryManager.java28
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java201
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java72
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java114
-rw-r--r--services/core/java/com/android/server/am/EventLogTags.logtags2
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java71
-rw-r--r--services/core/java/com/android/server/connectivity/Nat464Xlat.java9
-rw-r--r--services/core/java/com/android/server/connectivity/Tethering.java5
-rw-r--r--services/core/java/com/android/server/display/AutomaticBrightnessController.java17
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java2
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java31
-rw-r--r--services/core/java/com/android/server/display/LiveDisplayController.java823
-rw-r--r--services/core/java/com/android/server/dreams/DreamManagerService.java86
-rw-r--r--services/core/java/com/android/server/gesture/GestureInputFilter.java333
-rw-r--r--services/core/java/com/android/server/gesture/GestureService.java63
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java220
-rw-r--r--services/core/java/com/android/server/lights/Light.java2
-rw-r--r--services/core/java/com/android/server/lights/LightsManager.java4
-rw-r--r--services/core/java/com/android/server/lights/LightsService.java31
-rw-r--r--services/core/java/com/android/server/location/GeoFencerBase.java147
-rw-r--r--services/core/java/com/android/server/location/GeoFencerProxy.java149
-rw-r--r--services/core/java/com/android/server/location/GpsLocationProvider.java4
-rw-r--r--services/core/java/com/android/server/net/NetPluginDelegate.java40
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java3
-rwxr-xr-x[-rw-r--r--]services/core/java/com/android/server/notification/NotificationManagerService.java451
-rw-r--r--services/core/java/com/android/server/pm/BasePermission.java2
-rw-r--r--services/core/java/com/android/server/pm/Installer.java39
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java1085
-rw-r--r--services/core/java/com/android/server/pm/PackageSettingBase.java38
-rwxr-xr-x[-rw-r--r--]services/core/java/com/android/server/pm/Settings.java315
-rw-r--r--services/core/java/com/android/server/policy/GlobalActions.java196
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java1023
-rw-r--r--services/core/java/com/android/server/policy/PolicyControl.java4
-rw-r--r--services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java10
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java344
-rw-r--r--services/core/java/com/android/server/power/ShutdownThread.java268
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java323
-rw-r--r--services/core/java/com/android/server/wm/Session.java28
-rw-r--r--services/core/java/com/android/server/wm/Watermark.java52
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java62
57 files changed, 8932 insertions, 589 deletions
diff --git a/services/core/java/com/android/server/AppOpsPolicy.java b/services/core/java/com/android/server/AppOpsPolicy.java
new file mode 100644
index 0000000..75bca05
--- /dev/null
+++ b/services/core/java/com/android/server/AppOpsPolicy.java
@@ -0,0 +1,441 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.server;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import com.android.internal.util.XmlUtils;
+
+public class AppOpsPolicy {
+ static final String TAG = "AppOpsPolicy";
+ static final boolean DEBUG = false;
+ final File mFile;
+ final Context mContext;
+ public static final int CONTROL_SHOW = 0;
+
+ public static final int CONTROL_NOSHOW = 1;
+
+ public static final int CONTROL_UNKNOWN = 2;
+
+ public static int stringToControl(String show) {
+ if ("true".equalsIgnoreCase(show)) {
+ return CONTROL_SHOW;
+ } else if ("false".equalsIgnoreCase(show)) {
+ return CONTROL_NOSHOW;
+ }
+ return CONTROL_UNKNOWN;
+ }
+
+ HashMap<String, PolicyPkg> mPolicy = new HashMap<String, PolicyPkg>();
+
+ public AppOpsPolicy(File file, Context context) {
+ super();
+ mFile = file;
+ mContext = context;
+ }
+
+ public final static class PolicyPkg extends SparseArray<PolicyOp> {
+ public String packageName;
+ public int mode;
+ public int show;
+ public String type;
+
+ public PolicyPkg(String packageName, int mode, int show, String type) {
+ this.packageName = packageName;
+ this.mode = mode;
+ this.show = show;
+ this.type = type;
+ }
+
+ @Override
+ public String toString() {
+ return "PolicyPkg [packageName=" + packageName + ", mode=" + mode
+ + ", show=" + show + ", type=" + type + "]";
+ }
+
+ }
+
+ public final static class PolicyOp {
+ public int op;
+ public int mode;
+ public int show;
+
+ public PolicyOp(int op, int mode, int show) {
+ this.op = op;
+ this.mode = mode;
+ this.show = show;
+ }
+
+ @Override
+ public String toString() {
+ return "PolicyOp [op=" + op + ", mode=" + mode + ", show=" + show
+ + "]";
+ }
+ }
+
+ void readPolicy() {
+ FileInputStream stream;
+ synchronized (mFile) {
+ try {
+ stream = new FileInputStream(mFile);
+ } catch (FileNotFoundException e) {
+ Slog.i(TAG, "App ops policy file (" + mFile.getPath()
+ + ") not found; Skipping.");
+ return;
+ }
+ boolean success = false;
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, null);
+ int type;
+ success = true;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ ;
+ }
+ if (type != XmlPullParser.START_TAG) {
+ throw new IllegalStateException("no start tag found");
+ }
+
+ int outerDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG
+ || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals("user-app")
+ || tagName.equals("system-app")) {
+ readDefaultPolicy(parser, tagName);
+ } else if (tagName.equals("application")) {
+ readApplicationPolicy(parser);
+ } else {
+ Slog.w(TAG, "Unknown element under <appops-policy>: "
+ + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ } catch (IllegalStateException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } catch (NullPointerException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } catch (XmlPullParserException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } catch (IndexOutOfBoundsException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } finally {
+ if (!success) {
+ mPolicy.clear();
+ }
+ try {
+ stream.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ private void readDefaultPolicy(XmlPullParser parser, String packageName)
+ throws NumberFormatException, XmlPullParserException, IOException {
+ if (!"user-app".equalsIgnoreCase(packageName)
+ && !"system-app".equalsIgnoreCase(packageName)) {
+ return;
+ }
+ int mode = AppOpsManager.stringToMode(parser.getAttributeValue(null,
+ "permission"));
+ int show = stringToControl(parser.getAttributeValue(null, "show"));
+ if (mode == AppOpsManager.MODE_ERRORED && show == CONTROL_UNKNOWN) {
+ return;
+ }
+ PolicyPkg pkg = this.mPolicy.get(packageName);
+ if (pkg == null) {
+ pkg = new PolicyPkg(packageName, mode, show, packageName);
+ this.mPolicy.put(packageName, pkg);
+ } else {
+ Slog.w(TAG, "Duplicate policy found for package: " + packageName
+ + " of type: " + packageName);
+ pkg.mode = mode;
+ pkg.show = show;
+ }
+ }
+
+ private void readApplicationPolicy(XmlPullParser parser)
+ throws NumberFormatException, XmlPullParserException, IOException {
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ String tagName = parser.getName();
+ if (tagName.equals("pkg")) {
+ readPkgPolicy(parser);
+ } else {
+ Slog.w(TAG,
+ "Unknown element under <application>: "
+ + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ }
+
+ private void readPkgPolicy(XmlPullParser parser)
+ throws NumberFormatException, XmlPullParserException, IOException {
+ String packageName = parser.getAttributeValue(null, "name");
+ if (packageName == null)
+ return;
+ String appType = parser.getAttributeValue(null, "type");
+ if (appType == null)
+ return;
+ int mode = AppOpsManager.stringToMode(parser.getAttributeValue(null,
+ "permission"));
+ int show = stringToControl(parser.getAttributeValue(null, "show"));
+ String key = packageName + "." + appType;
+ PolicyPkg pkg = this.mPolicy.get(key);
+ if (pkg == null) {
+ pkg = new PolicyPkg(packageName, mode, show, appType);
+ this.mPolicy.put(key, pkg);
+ } else {
+ Slog.w(TAG, "Duplicate policy found for package: " + packageName
+ + " of type: " + appType);
+ pkg.mode = mode;
+ pkg.show = show;
+ }
+
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ String tagName = parser.getName();
+ if (tagName.equals("op")) {
+ readOpPolicy(parser, pkg);
+ } else {
+ Slog.w(TAG, "Unknown element under <pkg>: " + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ }
+
+ private void readOpPolicy(XmlPullParser parser, PolicyPkg pkg)
+ throws NumberFormatException, XmlPullParserException, IOException {
+ if (pkg == null) {
+ return;
+ }
+ String opName = parser.getAttributeValue(null, "name");
+ if (opName == null) {
+ Slog.w(TAG, "Op name is null");
+ return;
+ }
+ int code = AppOpsManager.stringOpToOp(opName);
+ if (code == AppOpsManager.OP_NONE) {
+ Slog.w(TAG, "Unknown Op: " + opName);
+ return;
+ }
+ int mode = AppOpsManager.stringToMode(parser.getAttributeValue(null,
+ "permission"));
+ int show = stringToControl(parser.getAttributeValue(null, "show"));
+ if (mode == AppOpsManager.MODE_ERRORED && show == CONTROL_UNKNOWN) {
+ return;
+ }
+ PolicyOp op = pkg.get(code);
+ if (op == null) {
+ op = new PolicyOp(code, mode, show);
+ pkg.put(code, op);
+ } else {
+ Slog.w(TAG, "Duplicate policy found for package: "
+ + pkg.packageName + " type: " + pkg.type + " op: " + op.op);
+ op.mode = mode;
+ op.show = show;
+ }
+ }
+
+ void debugPoilcy() {
+ Iterator<Map.Entry<String, PolicyPkg>> iterator = mPolicy.entrySet()
+ .iterator();
+ while (iterator.hasNext()) {
+ String key = iterator.next().getKey();
+ if (DEBUG)
+ Slog.d(TAG, "Key: " + key);
+ PolicyPkg pkg = mPolicy.get(key);
+ if (pkg == null) {
+ if (DEBUG)
+ Slog.d(TAG, "Pkg is null for key: " + key);
+ continue;
+ }
+ if (DEBUG)
+ Slog.d(TAG, pkg.toString());
+ for (int i = 0; i < pkg.size(); i++) {
+ PolicyOp op = pkg.valueAt(i);
+ if (DEBUG)
+ Slog.d(TAG, op.toString());
+ }
+ }
+ }
+
+ private String getAppType(String packageName) {
+ String appType = null;
+ ApplicationInfo appInfo = null;
+ if (mContext != null) {
+ try {
+ appInfo = mContext.getPackageManager().getApplicationInfo(
+ packageName, 0);
+ } catch (NameNotFoundException e) {
+ appInfo = null;
+ }
+ if (appInfo != null) {
+ if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ appType = "system-app";
+ } else {
+ appType = "user-app";
+ }
+ }
+ } else {
+ Slog.e(TAG, "Context is null");
+ }
+ return appType;
+ }
+
+ public boolean isControlAllowed(int code, String packageName) {
+ boolean isShow = true;
+ int show = CONTROL_UNKNOWN;
+ PolicyPkg pkg;
+ String key;
+ String type;
+
+ if (mPolicy == null) {
+ return isShow;
+ }
+
+ type = getAppType(packageName);
+ if (type != null) {
+ key = type;
+ pkg = mPolicy.get(key);
+ if (pkg != null && pkg.show != CONTROL_UNKNOWN) {
+ show = pkg.show;
+ }
+ }
+ key = packageName;
+ if (type != null) {
+ key = key + "." + type;
+ }
+ pkg = mPolicy.get(key);
+ if (pkg != null) {
+ if (pkg.show != CONTROL_UNKNOWN) {
+ show = pkg.show;
+ }
+ PolicyOp op = pkg.get(code);
+ if (op != null) {
+ if (op.show != CONTROL_UNKNOWN) {
+ show = op.show;
+ }
+ }
+ }
+ if (show == CONTROL_NOSHOW) {
+ isShow = false;
+ }
+ return isShow;
+ }
+
+ public int getDefualtMode(int code, String packageName) {
+ int mode = AppOpsManager.MODE_ERRORED;
+ PolicyPkg pkg;
+ String key;
+ String type;
+
+ if (mPolicy == null) {
+ return mode;
+ }
+ if (DEBUG)
+ Slog.d(TAG, "Default mode requested for op=" + code + " package="
+ + packageName);
+ type = getAppType(packageName);
+ if (type != null) {
+ // Get value based on 'type'
+ key = type;
+ pkg = mPolicy.get(key);
+ if (pkg != null && pkg.mode != AppOpsManager.MODE_ERRORED) {
+ if (DEBUG)
+ Slog.d(TAG, "Setting value based on type: " + pkg);
+ mode = pkg.mode;
+ }
+ }
+ // Get value based on 'pkg'.
+ key = packageName;
+ if (type != null) {
+ key = key + "." + type;
+ }
+ pkg = mPolicy.get(key);
+ if (pkg != null) {
+ if (pkg.mode != AppOpsManager.MODE_ERRORED) {
+ if (DEBUG)
+ Slog.d(TAG, "Setting value based on packageName: " + pkg);
+ mode = pkg.mode;
+ }
+ // Get value base on 'op'
+ PolicyOp op = pkg.get(code);
+ if (op != null) {
+ if (op.mode != AppOpsManager.MODE_ERRORED) {
+ if (DEBUG)
+ Slog.d(TAG, "Setting value based on op: " + op);
+ mode = op.mode;
+ }
+ }
+ }
+ if (DEBUG)
+ Slog.d(TAG, "Returning mode=" + mode);
+ return mode;
+ }
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index c373fb8..5d0b4f8 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -1,4 +1,7 @@
/*
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -35,7 +38,9 @@ import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.Dialog;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
@@ -46,6 +51,7 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -68,6 +74,7 @@ import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
+import com.android.server.PermissionDialogReqQueue.PermissionDialogReq;
import libcore.util.EmptyArray;
import org.xmlpull.v1.XmlPullParser;
@@ -81,9 +88,23 @@ public class AppOpsService extends IAppOpsService.Stub {
// Write at most every 30 minutes.
static final long WRITE_DELAY = DEBUG ? 1000 : 30*60*1000;
+ // Location of policy file.
+ static final String DEFAULT_POLICY_FILE = "/system/etc/appops_policy.xml";
+
Context mContext;
final AtomicFile mFile;
final Handler mHandler;
+ final Looper mLooper;
+ final boolean mStrictEnable;
+ AppOpsPolicy mPolicy;
+
+ private static final int[] PRIVACY_GUARD_OP_STATES = new int[] {
+ AppOpsManager.OP_COARSE_LOCATION,
+ AppOpsManager.OP_READ_CALL_LOG,
+ AppOpsManager.OP_READ_CONTACTS,
+ AppOpsManager.OP_READ_CALENDAR,
+ AppOpsManager.OP_READ_SMS
+ };
boolean mWriteScheduled;
boolean mFastWriteScheduled;
@@ -105,6 +126,14 @@ public class AppOpsService extends IAppOpsService.Stub {
final SparseArray<UidState> mUidStates = new SparseArray<>();
+ private Runnable mSuSessionChangedRunner = new Runnable() {
+ @Override
+ public void run() {
+ mContext.sendBroadcastAsUser(new Intent(AppOpsManager.ACTION_SU_SESSION_CHANGED),
+ UserHandle.ALL);
+ }
+ };
+
private final SparseArray<boolean[]> mOpRestrictions = new SparseArray<boolean[]>();
private static final class UidState {
@@ -150,12 +179,20 @@ public class AppOpsService extends IAppOpsService.Stub {
public long time;
public long rejectTime;
public int nesting;
-
- public Op(int _uid, String _packageName, int _op) {
+ public int noteOpCount;
+ public int startOpCount;
+ public PermissionDialogReqQueue dialogReqQueue;
+ final ArrayList<IBinder> clientTokens;
+ public int allowedCount;
+ public int ignoredCount;
+
+ public Op(int _uid, String _packageName, int _op, int _mode) {
uid = _uid;
packageName = _packageName;
op = _op;
- mode = AppOpsManager.opToDefaultMode(op);
+ mode = _mode;
+ dialogReqQueue = new PermissionDialogReqQueue();
+ clientTokens = new ArrayList<IBinder>();
}
}
@@ -227,17 +264,27 @@ public class AppOpsService extends IAppOpsService.Stub {
}
mClients.remove(mAppToken);
}
+
+ // We cannot broadcast on the synchronized block above because the broadcast might
+ // trigger another appop call that eventually arrives here from a different thread,
+ // causing a deadlock.
+ for (int i=mStartedOps.size()-1; i>=0; i--) {
+ broadcastOpIfNeeded(mStartedOps.get(i).op);
+ }
}
}
public AppOpsService(File storagePath, Handler handler) {
mFile = new AtomicFile(storagePath);
mHandler = handler;
+ mLooper = Looper.myLooper();
+ mStrictEnable = AppOpsManager.isStrictEnable();
readState();
}
public void publish(Context context) {
mContext = context;
+ readPolicy();
ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder());
}
@@ -373,7 +420,7 @@ public class AppOpsService extends IAppOpsService.Stub {
Op curOp = pkgOps.valueAt(j);
resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
curOp.rejectTime, curOp.duration, curOp.proxyUid,
- curOp.proxyPackageName));
+ curOp.proxyPackageName, curOp.allowedCount, curOp.ignoredCount));
}
} else {
for (int j=0; j<ops.length; j++) {
@@ -384,7 +431,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
curOp.rejectTime, curOp.duration, curOp.proxyUid,
- curOp.proxyPackageName));
+ curOp.proxyPackageName, curOp.allowedCount, curOp.ignoredCount));
}
}
}
@@ -476,7 +523,8 @@ public class AppOpsService extends IAppOpsService.Stub {
code = AppOpsManager.opToSwitch(code);
synchronized (this) {
- final int defaultMode = AppOpsManager.opToDefaultMode(code);
+ final int defaultMode = AppOpsManager.opToDefaultMode(code,
+ AppOpsManager.isStrictOp(code));
UidState uidState = getUidStateLocked(uid, false);
if (uidState == null) {
@@ -604,7 +652,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
repCbs.addAll(cbs);
}
- if (mode == AppOpsManager.opToDefaultMode(op.op)) {
+ if (mode == getDefaultMode(code, uid, packageName)) {
// If going into the default mode, prune this op
// if there is nothing else interesting in it.
pruneOp(op, uid, packageName);
@@ -731,9 +779,11 @@ public class AppOpsService extends IAppOpsService.Stub {
Ops pkgOps = ent.getValue();
for (int j=pkgOps.size()-1; j>=0; j--) {
Op curOp = pkgOps.valueAt(j);
+ int defaultMode = getDefaultMode(curOp.op, curOp.uid,
+ curOp.packageName);
if (AppOpsManager.opAllowsReset(curOp.op)
- && curOp.mode != AppOpsManager.opToDefaultMode(curOp.op)) {
- curOp.mode = AppOpsManager.opToDefaultMode(curOp.op);
+ && curOp.mode != defaultMode) {
+ curOp.mode = defaultMode;
changed = true;
callbacks = addCallbacks(callbacks, packageName, curOp.op,
mOpModeWatchers.get(curOp.op));
@@ -854,7 +904,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
Op op = getOpLocked(code, uid, packageName, false);
if (op == null) {
- return AppOpsManager.opToDefaultMode(code);
+ return getDefaultMode(code, uid, packageName);
}
return op.mode;
}
@@ -945,6 +995,7 @@ public class AppOpsService extends IAppOpsService.Stub {
private int noteOperationUnchecked(int code, int uid, String packageName,
int proxyUid, String proxyPackageName) {
+ final PermissionDialogReq req;
synchronized (this) {
Ops ops = getOpsLocked(uid, packageName, true);
if (ops == null) {
@@ -954,6 +1005,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
Op op = getOpLocked(ops, code, true);
if (isOpRestricted(uid, code, packageName)) {
+ op.ignoredCount++;
return AppOpsManager.MODE_IGNORED;
}
if (op.duration == -1) {
@@ -974,24 +1026,51 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
- if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
- if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
- + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
+ if (switchOp.mode != AppOpsManager.MODE_ALLOWED
+ && switchOp.mode != AppOpsManager.MODE_ASK) {
+ if (DEBUG)
+ Log.d(TAG, "noteOperation: reject #" + op.mode
+ + " for code " + switchCode + " (" + code
+ + ") uid " + uid + " package " + packageName);
op.rejectTime = System.currentTimeMillis();
+ op.ignoredCount++;
return switchOp.mode;
+ } else if (switchOp.mode == AppOpsManager.MODE_ALLOWED) {
+ if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
+ + " package " + packageName);
+ op.time = System.currentTimeMillis();
+ op.rejectTime = 0;
+ op.proxyUid = proxyUid;
+ op.proxyPackageName = proxyPackageName;
+ op.allowedCount++;
+ broadcastOpIfNeeded(code);
+ return AppOpsManager.MODE_ALLOWED;
+
+ } else {
+ if (Looper.myLooper() == mLooper) {
+ Log.e(TAG,
+ "noteOperation: This method will deadlock if called from the main thread. (Code: "
+ + code
+ + " uid: "
+ + uid
+ + " package: "
+ + packageName + ")");
+ return switchOp.mode;
+ }
+ op.noteOpCount++;
+ req = askOperationLocked(code, uid, packageName, switchOp);
}
- if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
- + " package " + packageName);
- op.time = System.currentTimeMillis();
- op.rejectTime = 0;
- op.proxyUid = proxyUid;
- op.proxyPackageName = proxyPackageName;
- return AppOpsManager.MODE_ALLOWED;
}
+
+ int result = req.get();
+ broadcastOpIfNeeded(code);
+ return result;
}
@Override
- public int startOperation(IBinder token, int code, int uid, String packageName) {
+ public int startOperation(IBinder token, int code, int uid,
+ String packageName) {
+ final PermissionDialogReq req;
verifyIncomingUid(uid);
verifyIncomingOp(code);
ClientState client = (ClientState)token;
@@ -1004,6 +1083,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
Op op = getOpLocked(ops, code, true);
if (isOpRestricted(uid, code, packageName)) {
+ op.ignoredCount++;
return AppOpsManager.MODE_IGNORED;
}
final int switchCode = AppOpsManager.opToSwitch(code);
@@ -1019,25 +1099,51 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
- if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
- if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code "
- + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
+ if (switchOp.mode != AppOpsManager.MODE_ALLOWED
+ && switchOp.mode != AppOpsManager.MODE_ASK) {
+ if (DEBUG)
+ Log.d(TAG, "startOperation: reject #" + op.mode
+ + " for code " + switchCode + " (" + code
+ + ") uid " + uid + " package " + packageName);
op.rejectTime = System.currentTimeMillis();
+ op.ignoredCount++;
return switchOp.mode;
+ } else if (switchOp.mode == AppOpsManager.MODE_ALLOWED) {
+ if (DEBUG)
+ Log.d(TAG, "startOperation: allowing code " + code
+ + " uid " + uid + " package " + packageName);
+ if (op.nesting == 0) {
+ op.time = System.currentTimeMillis();
+ op.rejectTime = 0;
+ op.duration = -1;
+ op.allowedCount++;
+ }
+ op.nesting++;
+ if (client.mStartedOps != null) {
+ client.mStartedOps.add(op);
+ }
+ broadcastOpIfNeeded(code);
+ return AppOpsManager.MODE_ALLOWED;
+ } else {
+ if (Looper.myLooper() == mLooper) {
+ Log.e(TAG,
+ "startOperation: This method will deadlock if called from the main thread. (Code: "
+ + code
+ + " uid: "
+ + uid
+ + " package: "
+ + packageName + ")");
+ return switchOp.mode;
+ }
+ op.startOpCount++;
+ IBinder clientToken = client.mAppToken;
+ op.clientTokens.add(clientToken);
+ req = askOperationLocked(code, uid, packageName, switchOp);
}
- if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid
- + " package " + packageName);
- if (op.nesting == 0) {
- op.time = System.currentTimeMillis();
- op.rejectTime = 0;
- op.duration = -1;
- }
- op.nesting++;
- if (client.mStartedOps != null) {
- client.mStartedOps.add(op);
- }
- return AppOpsManager.MODE_ALLOWED;
}
+ int result = req.get();
+ broadcastOpIfNeeded(code);
+ return result;
}
@Override
@@ -1058,6 +1164,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
finishOperationLocked(op);
}
+ broadcastOpIfNeeded(code);
}
@Override
@@ -1082,6 +1189,10 @@ public class AppOpsService extends IAppOpsService.Stub {
}
private void verifyIncomingUid(int uid) {
+ if (Binder.getCallingUid() == 0) {
+ // Allow root to delegate uid operations.
+ return;
+ }
if (uid == Binder.getCallingUid()) {
return;
}
@@ -1116,6 +1227,9 @@ public class AppOpsService extends IAppOpsService.Stub {
packageName = "root";
} else if (uid == Process.SHELL_UID) {
packageName = "com.android.shell";
+ } else if (uid == Process.SYSTEM_UID) {
+ if (packageName == null)
+ packageName = "android";
}
return getOpsRawLocked(uid, packageName, edit);
}
@@ -1203,12 +1317,14 @@ public class AppOpsService extends IAppOpsService.Stub {
}
private Op getOpLocked(Ops ops, int code, boolean edit) {
+ int mode;
Op op = ops.get(code);
if (op == null) {
if (!edit) {
return null;
}
- op = new Op(ops.uidState.uid, ops.packageName, code);
+ mode = getDefaultMode(code, ops.uidState.uid, ops.packageName);
+ op = new Op(ops.uidState.uid, ops.packageName, code, mode);
ops.put(code, op);
}
if (edit) {
@@ -1388,10 +1504,32 @@ public class AppOpsService extends IAppOpsService.Stub {
String tagName = parser.getName();
if (tagName.equals("op")) {
- Op op = new Op(uid, pkgName, Integer.parseInt(parser.getAttributeValue(null, "n")));
+ int code = Integer
+ .parseInt(parser.getAttributeValue(null, "n"));
+ // use op name string if it exists
+ String codeNameStr = parser.getAttributeValue(null, "ns");
+ if (codeNameStr != null) {
+ // returns OP_NONE if it could not be mapped
+ code = AppOpsManager.nameToOp(codeNameStr);
+ }
+ // skip op codes that are out of bounds
+ if (code == AppOpsManager.OP_NONE
+ || code >= AppOpsManager._NUM_OP) {
+ continue;
+ }
+ Op op = new Op(uid, pkgName, code, AppOpsManager.MODE_ERRORED);
String mode = parser.getAttributeValue(null, "m");
if (mode != null) {
op.mode = Integer.parseInt(mode);
+ } else {
+ String sDefualtMode = parser.getAttributeValue(null, "dm");
+ int defaultMode;
+ if (sDefualtMode != null) {
+ defaultMode = Integer.parseInt(sDefualtMode);
+ } else {
+ defaultMode = getDefaultMode(code, uid, pkgName);
+ }
+ op.mode = defaultMode;
}
String time = parser.getAttributeValue(null, "t");
if (time != null) {
@@ -1413,7 +1551,14 @@ public class AppOpsService extends IAppOpsService.Stub {
if (proxyPackageName != null) {
op.proxyPackageName = proxyPackageName;
}
-
+ String allowed = parser.getAttributeValue(null, "ac");
+ if (allowed != null) {
+ op.allowedCount = Integer.parseInt(allowed);
+ }
+ String ignored = parser.getAttributeValue(null, "ic");
+ if (ignored != null) {
+ op.ignoredCount = Integer.parseInt(ignored);
+ }
UidState uidState = getUidStateLocked(uid, true);
if (uidState.pkgOps == null) {
uidState.pkgOps = new ArrayMap<>();
@@ -1500,8 +1645,13 @@ public class AppOpsService extends IAppOpsService.Stub {
AppOpsManager.OpEntry op = ops.get(j);
out.startTag(null, "op");
out.attribute(null, "n", Integer.toString(op.getOp()));
- if (op.getMode() != AppOpsManager.opToDefaultMode(op.getOp())) {
+ out.attribute(null, "ns", AppOpsManager.opToName(op.getOp()));
+ int defaultMode = getDefaultMode(op.getOp(),
+ pkg.getUid(), pkg.getPackageName());
+ if (op.getMode() != defaultMode) {
out.attribute(null, "m", Integer.toString(op.getMode()));
+ } else {
+ out.attribute(null, "dm", Integer.toString(defaultMode));
}
long time = op.getTime();
if (time != 0) {
@@ -1523,6 +1673,14 @@ public class AppOpsService extends IAppOpsService.Stub {
if (proxyPackageName != null) {
out.attribute(null, "pp", proxyPackageName);
}
+ int allowed = op.getAllowedCount();
+ if (allowed != 0) {
+ out.attribute(null, "ac", Integer.toString(allowed));
+ }
+ int ignored = op.getIgnoredCount();
+ if (ignored != 0) {
+ out.attribute(null, "ic", Integer.toString(ignored));
+ }
out.endTag(null, "op");
}
out.endTag(null, "uid");
@@ -1766,14 +1924,181 @@ public class AppOpsService extends IAppOpsService.Stub {
private void checkSystemUid(String function) {
int uid = Binder.getCallingUid();
if (uid != Process.SYSTEM_UID) {
- throw new SecurityException(function + " must by called by the system");
+ throw new SecurityException(function
+ + " must by called by the system");
+ }
+ }
+
+ final class AskRunnable implements Runnable {
+ final int code;
+ final int uid;
+ final String packageName;
+ final Op op;
+ final PermissionDialogReq request;
+
+ public AskRunnable(int code, int uid, String packageName, Op op,
+ PermissionDialogReq request) {
+ super();
+ this.code = code;
+ this.uid = uid;
+ this.packageName = packageName;
+ this.op = op;
+ this.request = request;
+ }
+
+ @Override
+ public void run() {
+ PermissionDialog permDialog = null;
+ synchronized (AppOpsService.this) {
+ Log.e(TAG, "Creating dialog box");
+ op.dialogReqQueue.register(request);
+ if (op.dialogReqQueue.getDialog() == null) {
+ permDialog = new PermissionDialog(mContext,
+ AppOpsService.this, code, uid, packageName);
+ op.dialogReqQueue.setDialog(permDialog);
+ }
+ }
+ if (permDialog != null) {
+ permDialog.show();
+ }
+ }
+ }
+
+ private PermissionDialogReq askOperationLocked(int code, int uid,
+ String packageName, Op op) {
+ PermissionDialogReq request = new PermissionDialogReq();
+ mHandler.post(new AskRunnable(code, uid, packageName, op, request));
+ return request;
+ }
+
+ private int getDefaultMode(int code, int uid, String packageName) {
+ int mode = AppOpsManager.opToDefaultMode(code,
+ isStrict(code, uid, packageName));
+ if (AppOpsManager.isStrictOp(code) && mPolicy != null) {
+ int policyMode = mPolicy.getDefualtMode(code, packageName);
+ if (policyMode != AppOpsManager.MODE_ERRORED) {
+ mode = policyMode;
+ }
+ }
+ return mode;
+ }
+
+ private boolean isStrict(int code, int uid, String packageName) {
+ if (!mStrictEnable)
+ return false;
+
+ return UserHandle.isApp(uid);
+ }
+
+ private void printOperationLocked(Op op, int mode, String operation) {
+ if(op != null) {
+ int switchCode = AppOpsManager.opToSwitch(op.op);
+ if (mode == AppOpsManager.MODE_IGNORED) {
+ if (DEBUG) Log.d(TAG, operation + ": reject #" + mode + " for code "
+ + switchCode + " (" + op.op + ") uid " + op.uid + " package "
+ + op.packageName);
+ } else if (mode == AppOpsManager.MODE_ALLOWED) {
+ if (DEBUG) Log.d(TAG, operation + ": allowing code " + op.op + " uid "
+ + op.uid
+ + " package " + op.packageName);
+ }
+ }
+ }
+
+ private void recordOperationLocked(int code, int uid, String packageName,
+ int mode) {
+ Op op = getOpLocked(code, uid, packageName, false);
+ if(op != null) {
+ if(op.noteOpCount != 0)
+ printOperationLocked(op, mode, "noteOperartion");
+ if(op.startOpCount != 0)
+ printOperationLocked(op, mode, "startOperation");
+ if (mode == AppOpsManager.MODE_IGNORED) {
+ op.rejectTime = System.currentTimeMillis();
+ } else if (mode == AppOpsManager.MODE_ALLOWED) {
+ if(op.noteOpCount != 0) {
+ op.time = System.currentTimeMillis();
+ op.rejectTime = 0;
+ }
+ if(op.startOpCount != 0) {
+ if(op.nesting == 0) {
+ op.time = System.currentTimeMillis();
+ op.rejectTime = 0;
+ op.duration = -1;
+ }
+ op.nesting = op.nesting + op.startOpCount;
+ while(op.clientTokens.size() != 0) {
+ IBinder clientToken = op.clientTokens.get(0);
+ ClientState client = mClients.get(clientToken);
+ if (client != null) {
+ if (client.mStartedOps != null) {
+ client.mStartedOps.add(op);
+ }
+ }
+ op.clientTokens.remove(0);
+ }
+ }
+ }
+ op.clientTokens.clear();
+ op.startOpCount = 0;
+ op.noteOpCount = 0;
+ }
+ }
+
+ public void notifyOperation(int code, int uid, String packageName,
+ int mode, boolean remember) {
+ verifyIncomingUid(uid);
+ verifyIncomingOp(code);
+ ArrayList<Callback> repCbs = null;
+ int switchCode = AppOpsManager.opToSwitch(code);
+ synchronized (this) {
+ recordOperationLocked(code, uid, packageName, mode);
+ Op op = getOpLocked(switchCode, uid, packageName, true);
+ if (op != null) {
+ // Send result to all waiting client
+ if (op.dialogReqQueue.getDialog() != null) {
+ op.dialogReqQueue.notifyAll(mode);
+ op.dialogReqQueue.setDialog(null);
+ }
+ if (remember && op.mode != mode) {
+ op.mode = mode;
+ ArrayList<Callback> cbs = mOpModeWatchers.get(switchCode);
+ if (cbs != null) {
+ if (repCbs == null) {
+ repCbs = new ArrayList<Callback>();
+ }
+ repCbs.addAll(cbs);
+ }
+ cbs = mPackageModeWatchers.get(packageName);
+ if (cbs != null) {
+ if (repCbs == null) {
+ repCbs = new ArrayList<Callback>();
+ }
+ repCbs.addAll(cbs);
+ }
+ if (mode == getDefaultMode(op.op, op.uid, op.packageName)) {
+ // If going into the default mode, prune this op
+ // if there is nothing else interesting in it.
+ pruneOp(op, uid, packageName);
+ }
+ scheduleWriteLocked();
+ }
+ }
+ }
+ if (repCbs != null) {
+ for (int i = 0; i < repCbs.size(); i++) {
+ try {
+ repCbs.get(i).mCallback.opChanged(switchCode, packageName);
+ } catch (RemoteException e) {
+ }
+ }
}
}
private static String[] getPackagesForUid(int uid) {
String[] packageNames = null;
try {
- packageNames= AppGlobals.getPackageManager().getPackagesForUid(uid);
+ packageNames = AppGlobals.getPackageManager().getPackagesForUid(uid);
} catch (RemoteException e) {
/* ignore - local call */
}
@@ -1782,4 +2107,75 @@ public class AppOpsService extends IAppOpsService.Stub {
}
return packageNames;
}
+
+ private void broadcastOpIfNeeded(int op) {
+ switch (op) {
+ case AppOpsManager.OP_SU:
+ mHandler.post(mSuSessionChangedRunner);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void readPolicy() {
+ if (mStrictEnable) {
+ mPolicy = new AppOpsPolicy(new File(DEFAULT_POLICY_FILE), mContext);
+ mPolicy.readPolicy();
+ mPolicy.debugPoilcy();
+ } else {
+ mPolicy = null;
+ }
+ }
+
+ public boolean isControlAllowed(int code, String packageName) {
+ boolean isShow = true;
+ if (mPolicy != null) {
+ isShow = mPolicy.isControlAllowed(code, packageName);
+ }
+ return isShow;
+ }
+
+ @Override
+ public boolean getPrivacyGuardSettingForPackage(int uid, String packageName) {
+ for (int op : PRIVACY_GUARD_OP_STATES) {
+ int switchOp = AppOpsManager.opToSwitch(op);
+ int mode = checkOperation(op, uid, packageName);
+ if (mode != AppOpsManager.MODE_ALLOWED && mode != AppOpsManager.MODE_IGNORED) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void setPrivacyGuardSettingForPackage(int uid, String packageName, boolean state) {
+ for (int op : PRIVACY_GUARD_OP_STATES) {
+ int switchOp = AppOpsManager.opToSwitch(op);
+ setMode(switchOp, uid, packageName, state
+ ? AppOpsManager.MODE_ASK : AppOpsManager.MODE_ALLOWED);
+ }
+ }
+
+ @Override
+ public void resetCounters() {
+ mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
+ Binder.getCallingPid(), Binder.getCallingUid(), null);
+ synchronized (this) {
+ for (int i=0; i<mUidStates.size(); i++) {
+ final UidState uidState = mUidStates.valueAt(i);
+ for (Map.Entry<String, Ops> ent : uidState.pkgOps.entrySet()) {
+ String packageName = ent.getKey();
+ Ops pkgOps = ent.getValue();
+ for (int j=0; j<pkgOps.size(); j++) {
+ Op curOp = pkgOps.valueAt(j);
+ curOp.allowedCount = 0;
+ curOp.ignoredCount = 0;
+ }
+ }
+ }
+ // ensure the counter reset persists
+ scheduleWriteLocked();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/AssetAtlasService.java b/services/core/java/com/android/server/AssetAtlasService.java
index 4569dae..ff4456e 100644
--- a/services/core/java/com/android/server/AssetAtlasService.java
+++ b/services/core/java/com/android/server/AssetAtlasService.java
@@ -391,6 +391,11 @@ public class AssetAtlasService extends IAssetAtlas.Stub {
}
}
+ if (results.size() == 0) {
+ if (DEBUG_ATLAS) Log.w(LOG_TAG, "No atlas configuration found!");
+ return null;
+ }
+
// Maximize the number of packed bitmaps, minimize the texture size
Collections.sort(results, new Comparator<WorkerResult>() {
@Override
diff --git a/services/core/java/com/android/server/BasePermissionDialog.java b/services/core/java/com/android/server/BasePermissionDialog.java
new file mode 100644
index 0000000..e3dbcda
--- /dev/null
+++ b/services/core/java/com/android/server/BasePermissionDialog.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright (C) 2006 The Android Open Source 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 com.android.server;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.view.KeyEvent;
+import android.view.WindowManager;
+import android.widget.Button;
+
+import com.android.internal.R;
+
+public class BasePermissionDialog extends AlertDialog {
+ public BasePermissionDialog(Context context) {
+ super(context, com.android.internal.R.style.Theme_Dialog_AppError);
+ getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+ WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+ WindowManager.LayoutParams attrs = getWindow().getAttributes();
+ attrs.setTitle("Permission Dialog");
+ getWindow().setAttributes(attrs);
+ setIconAttribute(R.attr.alertDialogIcon);
+ }
+
+ public void onStart() {
+ super.onStart();
+ setEnabled(false);
+ mHandler.sendMessage(mHandler.obtainMessage(0));
+ }
+
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (mConsuming) {
+ // Slog.i(TAG, "Consuming: " + event);
+ return true;
+ }
+ // Slog.i(TAG, "Dispatching: " + event);
+ return super.dispatchKeyEvent(event);
+ }
+
+ private void setEnabled(boolean enabled) {
+ Button b = (Button) findViewById(R.id.button1);
+ if (b != null) {
+ b.setEnabled(enabled);
+ }
+ b = (Button) findViewById(R.id.button2);
+ if (b != null) {
+ b.setEnabled(enabled);
+ }
+ b = (Button) findViewById(R.id.button3);
+ if (b != null) {
+ b.setEnabled(enabled);
+ }
+ }
+
+ private Handler mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ if (msg.what == 0) {
+ mConsuming = false;
+ setEnabled(true);
+ }
+ }
+ };
+
+ private boolean mConsuming = true;
+}
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index c3200fe..8a84246 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -29,6 +29,8 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.database.ContentObserver;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
import android.os.BatteryProperties;
@@ -97,6 +99,9 @@ public final class BatteryService extends SystemService {
private static final int BATTERY_SCALE = 100; // battery capacity is a percentage
+ // notification light maximum brightness value to use
+ private static final int LIGHT_BRIGHTNESS_MAXIMUM = 255;
+
// Used locally for determining when to make a last ditch effort to log
// discharge stats before the device dies.
private int mCriticalBatteryLevel;
@@ -129,6 +134,12 @@ public final class BatteryService extends SystemService {
private int mInvalidCharger;
private int mLastInvalidCharger;
+ private boolean mAdjustableNotificationLedBrightness;
+ private int mNotificationLedBrightnessLevel = LIGHT_BRIGHTNESS_MAXIMUM;
+
+ private boolean mMultipleNotificationLeds;
+ private boolean mMultipleLedsEnabled = false;
+
private int mLowBatteryWarningLevel;
private int mLowBatteryCloseWarningLevel;
private int mShutdownBatteryTemperature;
@@ -144,6 +155,13 @@ public final class BatteryService extends SystemService {
private boolean mUpdatesStopped;
private Led mLed;
+ // Disable LED until SettingsObserver can be started
+ private boolean mLightEnabled = false;
+ private boolean mLedPulseEnabled;
+ private int mBatteryLowARGB;
+ private int mBatteryMediumARGB;
+ private int mBatteryFullARGB;
+ private boolean mMultiColorLed;
private boolean mSentLowBatteryBroadcast = false;
@@ -205,6 +223,9 @@ public final class BatteryService extends SystemService {
false, obs, UserHandle.USER_ALL);
updateBatteryWarningLevelLocked();
}
+ } else if (phase == PHASE_BOOT_COMPLETED) {
+ SettingsObserver observer = new SettingsObserver(new Handler());
+ observer.observe();
}
}
@@ -261,8 +282,10 @@ public final class BatteryService extends SystemService {
private void shutdownIfNoPowerLocked() {
// shut down gracefully if our battery is critically low and we are not powered.
+ // or the battery voltage is decreasing (consumption rate higher than charging rate)
// wait until the system has booted before attempting to display the shutdown dialog.
- if (mBatteryProps.batteryLevel == 0 && !isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)) {
+ if (mBatteryProps.batteryLevel == 0 && (!isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY) ||
+ mBatteryProps.batteryVoltage < mLastBatteryVoltage) ) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -716,24 +739,31 @@ public final class BatteryService extends SystemService {
}
};
+ private synchronized void updateLedPulse() {
+ mLed.updateLightsLocked();
+ }
+
private final class Led {
private final Light mBatteryLight;
- private final int mBatteryLowARGB;
- private final int mBatteryMediumARGB;
- private final int mBatteryFullARGB;
private final int mBatteryLedOn;
private final int mBatteryLedOff;
public Led(Context context, LightsManager lights) {
mBatteryLight = lights.getLight(LightsManager.LIGHT_ID_BATTERY);
- mBatteryLowARGB = context.getResources().getInteger(
- com.android.internal.R.integer.config_notificationsBatteryLowARGB);
- mBatteryMediumARGB = context.getResources().getInteger(
- com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
- mBatteryFullARGB = context.getResources().getInteger(
- com.android.internal.R.integer.config_notificationsBatteryFullARGB);
+ // Does the Device support changing battery LED colors?
+ mMultiColorLed = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_multiColorBatteryLed);
+
+ // Is the notification LED brightness changeable ?
+ mAdjustableNotificationLedBrightness = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_adjustableNotificationLedBrightness);
+
+ // Does the Device have multiple LEDs ?
+ mMultipleNotificationLeds = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_multipleNotificationLeds);
+
mBatteryLedOn = context.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryLedOn);
mBatteryLedOff = context.getResources().getInteger(
@@ -772,21 +802,37 @@ public final class BatteryService extends SystemService {
* Synchronize on BatteryService.
*/
public void updateLightsLocked() {
+ // mBatteryProps could be null on startup (called by SettingsObserver)
+ if (mBatteryProps == null) {
+ Slog.w(TAG, "updateLightsLocked: mBatteryProps is null; skipping");
+ return;
+ }
+
final int level = mBatteryProps.batteryLevel;
final int status = mBatteryProps.batteryStatus;
- if (level < mLowBatteryWarningLevel) {
+ if (!mLightEnabled) {
+ // No lights if explicitly disabled
+ mBatteryLight.turnOff();
+ } else if (level < mLowBatteryWarningLevel) {
+ mBatteryLight.setModes(mNotificationLedBrightnessLevel,
+ mMultipleLedsEnabled);
if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
- // Solid red when battery is charging
+ // Battery is charging and low
mBatteryLight.setColor(mBatteryLowARGB);
- } else {
- // Flash red when battery is low and not charging
+ } else if (mLedPulseEnabled) {
+ // Battery is low and not charging
mBatteryLight.setFlashing(mBatteryLowARGB, Light.LIGHT_FLASH_TIMED,
mBatteryLedOn, mBatteryLedOff);
+ } else {
+ // "Pulse low battery light" is disabled, no lights.
+ mBatteryLight.turnOff();
}
} else if (status == BatteryManager.BATTERY_STATUS_CHARGING
|| status == BatteryManager.BATTERY_STATUS_FULL) {
+ mBatteryLight.setModes(mNotificationLedBrightnessLevel,
+ mMultipleLedsEnabled);
if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {
- // Solid green when full or charging and nearly full
+ // Battery is full or charging and nearly full
mBatteryLight.setColor(mBatteryFullARGB);
} else {
if (isHvdcpPresent()) {
@@ -794,7 +840,7 @@ public final class BatteryService extends SystemService {
mBatteryLight.setFlashing(mBatteryMediumARGB, Light.LIGHT_FLASH_TIMED,
mBatteryLedOn, mBatteryLedOn);
} else {
- // Solid orange when charging and halfway full
+ // Battery is charging and halfway full
mBatteryLight.setColor(mBatteryMediumARGB);
}
}
@@ -869,4 +915,96 @@ public final class BatteryService extends SystemService {
}
}
}
+
+ class SettingsObserver extends ContentObserver {
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ void observe() {
+ ContentResolver resolver = mContext.getContentResolver();
+
+ // Battery light enabled
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.BATTERY_LIGHT_ENABLED), false, this, UserHandle.USER_ALL);
+
+ // Low battery pulse
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.BATTERY_LIGHT_PULSE), false, this, UserHandle.USER_ALL);
+
+ // Notification LED brightness
+ if (mAdjustableNotificationLedBrightness) {
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.NOTIFICATION_LIGHT_BRIGHTNESS_LEVEL),
+ false, this, UserHandle.USER_ALL);
+ }
+
+ // Multiple LEDs enabled
+ if (mMultipleNotificationLeds) {
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.NOTIFICATION_LIGHT_MULTIPLE_LEDS_ENABLE),
+ false, this, UserHandle.USER_ALL);
+ }
+
+ // Light colors
+ if (mMultiColorLed) {
+ // Register observer if we have a multi color led
+ resolver.registerContentObserver(
+ Settings.System.getUriFor(Settings.System.BATTERY_LIGHT_LOW_COLOR),
+ false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(
+ Settings.System.getUriFor(Settings.System.BATTERY_LIGHT_MEDIUM_COLOR),
+ false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(
+ Settings.System.getUriFor(Settings.System.BATTERY_LIGHT_FULL_COLOR),
+ false, this, UserHandle.USER_ALL);
+ }
+
+ update();
+ }
+
+ @Override public void onChange(boolean selfChange) {
+ update();
+ }
+
+ public void update() {
+ ContentResolver resolver = mContext.getContentResolver();
+ Resources res = mContext.getResources();
+
+ // Battery light enabled
+ mLightEnabled = Settings.System.getInt(resolver,
+ Settings.System.BATTERY_LIGHT_ENABLED, 1) != 0;
+
+ // Low battery pulse
+ mLedPulseEnabled = Settings.System.getInt(resolver,
+ Settings.System.BATTERY_LIGHT_PULSE, 1) != 0;
+
+ // Light colors
+ mBatteryLowARGB = Settings.System.getInt(resolver,
+ Settings.System.BATTERY_LIGHT_LOW_COLOR, res.getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryLowARGB));
+ mBatteryMediumARGB = Settings.System.getInt(resolver,
+ Settings.System.BATTERY_LIGHT_MEDIUM_COLOR, res.getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryMediumARGB));
+ mBatteryFullARGB = Settings.System.getInt(resolver,
+ Settings.System.BATTERY_LIGHT_FULL_COLOR, res.getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryFullARGB));
+
+ // Notification LED brightness
+ if (mAdjustableNotificationLedBrightness) {
+ mNotificationLedBrightnessLevel = Settings.System.getInt(resolver,
+ Settings.System.NOTIFICATION_LIGHT_BRIGHTNESS_LEVEL,
+ LIGHT_BRIGHTNESS_MAXIMUM);
+ }
+
+ // Multiple LEDs enabled
+ if (mMultipleNotificationLeds) {
+ mMultipleLedsEnabled = Settings.System.getInt(resolver,
+ Settings.System.NOTIFICATION_LIGHT_MULTIPLE_LEDS_ENABLE,
+ mMultipleNotificationLeds ? 1 : 0) != 0;
+ }
+
+ updateLedPulse();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index fd7b050..52a45f6 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -1,4 +1,7 @@
/*
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,6 +21,7 @@ package com.android.server;
import android.Manifest;
import android.app.ActivityManager;
+import android.app.AppOpsManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
@@ -619,7 +623,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
return true;
}
- public boolean enable() {
+ public boolean enable(String callingPackage) {
if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
(!checkIfCallerIsForegroundUser())) {
Log.w(TAG,"enable(): not allowed for non-active and non system user");
@@ -633,6 +637,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
" mBinding = " + mBinding);
}
+ AppOpsManager appOps = (AppOpsManager) mContext
+ .getSystemService(Context.APP_OPS_SERVICE);
+ int callingUid = Binder.getCallingUid();
+ if (appOps.noteOp(AppOpsManager.OP_BLUETOOTH_CHANGE, callingUid,
+ callingPackage) != AppOpsManager.MODE_ALLOWED)
+ return false;
+
synchronized(mReceiver) {
mQuietEnableExternal = false;
mEnableExternal = true;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 63d8c45..ecfd45d 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -629,13 +629,18 @@ public class ConnectivityService extends IConnectivityManager.Stub
mTrackerHandler = new NetworkStateTrackerHandler(handlerThread.getLooper());
// setup our unique device name
- if (TextUtils.isEmpty(SystemProperties.get("net.hostname"))) {
+ String hostname = Settings.Secure.getString(context.getContentResolver(),
+ Settings.Secure.DEVICE_HOSTNAME);
+ if (TextUtils.isEmpty(hostname) &&
+ TextUtils.isEmpty(SystemProperties.get("net.hostname"))) {
String id = Settings.Secure.getString(context.getContentResolver(),
Settings.Secure.ANDROID_ID);
if (id != null && id.length() > 0) {
String name = new String("android-").concat(id);
SystemProperties.set("net.hostname", name);
}
+ } else {
+ SystemProperties.set("net.hostname", hostname);
}
// read our default dns server ip
@@ -4408,7 +4413,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
if (reapUnvalidatedNetworks == ReapUnvalidatedNetworks.REAP) {
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
- if (unneeded(nai)) {
+ if (unneeded(nai) && nai != newNetwork) {
if (DBG) log("Reaping " + nai.name());
teardownUnneededNetwork(nai);
}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 4e11070..9128280 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -89,6 +89,7 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
import android.text.style.SuggestionSpan;
@@ -465,6 +466,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this, userId);
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD), false, this, userId);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.STATUS_BAR_IME_SWITCHER),
+ false, new ContentObserver(mHandler) {
+ public void onChange(boolean selfChange) {
+ updateFromSettingsLocked(true);
+ }
+ }, userId);
+
mRegistered = true;
}
@@ -1074,8 +1083,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mStatusBar = statusBar;
statusBar.setIconVisibility("ime", false);
updateSystemUiLocked(mCurToken, mImeWindowVis, mBackDisposition);
- mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
- com.android.internal.R.bool.show_ongoing_ime_switcher);
if (mShowOngoingImeSwitcherForPhones) {
mWindowManagerService.setOnHardKeyboardStatusChangeListener(
mHardKeyboardListener);
@@ -1912,6 +1919,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mCurMethodId = null;
unbindCurrentMethodLocked(true, false);
}
+ // code to disable the CM Phone IME switcher with config_show_cmIMESwitcher set = false
+ try {
+ mShowOngoingImeSwitcherForPhones = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.STATUS_BAR_IME_SWITCHER) == 1;
+ } catch (SettingNotFoundException e) {
+ mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
+ com.android.internal.R.bool.config_show_cmIMESwitcher);
+ }
// Here is not the perfect place to reset the switching controller. Ideally
// mSwitchingController and mSettings should be able to share the same state.
// TODO: Make sure that mSwitchingController and mSettings are sharing the
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index bc1383c..00e86de 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -64,6 +64,7 @@ import android.location.Geofence;
import android.location.IGpsGeofenceHardware;
import android.location.IGpsMeasurementsListener;
import android.location.IGpsNavigationMessageListener;
+import android.location.GeoFenceParams;
import android.location.IGpsStatusListener;
import android.location.IGpsStatusProvider;
import android.location.ILocationListener;
@@ -90,6 +91,28 @@ import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+import com.android.internal.os.BackgroundThread;
+import com.android.server.location.FlpHardwareProvider;
+import com.android.server.location.FusedProxy;
+import com.android.server.location.GeocoderProxy;
+import com.android.server.location.GeofenceProxy;
+import com.android.server.location.GeofenceManager;
+import com.android.server.location.GeoFencerBase;
+import com.android.server.location.GeoFencerProxy;
+import com.android.server.location.GpsLocationProvider;
+import com.android.server.location.LocationBlacklist;
+import com.android.server.location.LocationFudger;
+import com.android.server.location.LocationProviderInterface;
+import com.android.server.location.LocationProviderProxy;
+import com.android.server.location.LocationRequestStatistics;
+import com.android.server.location.LocationRequestStatistics.PackageProviderKey;
+import com.android.server.location.LocationRequestStatistics.PackageStatistics;
+import com.android.server.location.MockProvider;
+import com.android.server.location.PassiveProvider;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -157,6 +180,8 @@ public class LocationManagerService extends ILocationManager.Stub {
private String mComboNlpPackageName;
private String mComboNlpReadyMarker;
private String mComboNlpScreenMarker;
+ private String mGeoFencerPackageName;
+ private GeoFencerBase mGeoFencer;
private PowerManager mPowerManager;
private UserManager mUserManager;
private GeocoderProxy mGeocodeProvider;
@@ -512,6 +537,15 @@ public class LocationManagerService extends ILocationManager.Stub {
Slog.e(TAG, "no geocoder provider found");
}
+ mGeoFencerPackageName = resources.getString(
+ com.android.internal.R.string.config_geofenceProvider);
+ if (mGeoFencerPackageName != null &&
+ mPackageManager.resolveService(new Intent(mGeoFencerPackageName), 0) != null){
+ mGeoFencer = GeoFencerProxy.getGeoFencerProxy(mContext, mGeoFencerPackageName);
+ } else {
+ mGeoFencer = null;
+ }
+
// bind to fused hardware provider if supported
// in devices without support, requesting an instance of FlpHardwareProvider will raise an
// exception, so make sure we only do that when supported
@@ -1622,9 +1656,11 @@ public class LocationManagerService extends ILocationManager.Stub {
checkLocationAccess(pid, uid, packageName, allowedResolutionLevel);
synchronized (mLock) {
- Receiver recevier = checkListenerOrIntentLocked(listener, intent, pid, uid,
+ Receiver receiver = checkListenerOrIntentLocked(listener, intent, pid, uid,
packageName, workSource, hideFromAppOps);
- requestLocationUpdatesLocked(sanitizedRequest, recevier, pid, uid, packageName);
+ if (receiver != null) {
+ requestLocationUpdatesLocked(sanitizedRequest, receiver, pid, uid, packageName);
+ }
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -1683,7 +1719,9 @@ public class LocationManagerService extends ILocationManager.Stub {
// providers may use public location API's, need to clear identity
long identity = Binder.clearCallingIdentity();
try {
- removeUpdatesLocked(receiver);
+ if (receiver != null) {
+ removeUpdatesLocked(receiver);
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -1822,8 +1860,20 @@ public class LocationManagerService extends ILocationManager.Stub {
}
long identity = Binder.clearCallingIdentity();
try {
- mGeofenceManager.addFence(sanitizedRequest, geofence, intent, allowedResolutionLevel,
- uid, packageName);
+ if (mGeoFencer != null) {
+ long expiration;
+ if (sanitizedRequest.getExpireAt() == Long.MAX_VALUE) {
+ expiration = -1; // -1 means forever
+ } else {
+ expiration = sanitizedRequest.getExpireAt() - SystemClock.elapsedRealtime();
+ }
+ mGeoFencer.add(new GeoFenceParams(uid, geofence.getLatitude(),
+ geofence.getLongitude(), geofence.getRadius(),
+ expiration, intent, packageName));
+ } else {
+ mGeofenceManager.addFence(sanitizedRequest, geofence, intent,
+ allowedResolutionLevel, uid, packageName);
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -1839,7 +1889,11 @@ public class LocationManagerService extends ILocationManager.Stub {
// geo-fence manager uses the public location API, need to clear identity
long identity = Binder.clearCallingIdentity();
try {
- mGeofenceManager.removeFence(geofence, intent);
+ if (mGeoFencer != null) {
+ mGeoFencer.remove(intent);
+ } else {
+ mGeofenceManager.removeFence(geofence, intent);
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -2731,6 +2785,10 @@ public class LocationManagerService extends ILocationManager.Stub {
mGeofenceManager.dump(pw);
+ if (mGeoFencer != null) {
+ mGeoFencer.dump(pw, "");
+ }
+
if (mEnabledProviders.size() > 0) {
pw.println(" Enabled Providers:");
for (String i : mEnabledProviders) {
diff --git a/services/core/java/com/android/server/MasterClearReceiver.java b/services/core/java/com/android/server/MasterClearReceiver.java
index 1653db9..23556c0 100644
--- a/services/core/java/com/android/server/MasterClearReceiver.java
+++ b/services/core/java/com/android/server/MasterClearReceiver.java
@@ -34,6 +34,9 @@ import java.io.IOException;
public class MasterClearReceiver extends BroadcastReceiver {
private static final String TAG = "MasterClear";
+ /* {@hide} */
+ public static final String EXTRA_WIPE_MEDIA = "wipe_media";
+
@Override
public void onReceive(final Context context, final Intent intent) {
if (intent.getAction().equals(Intent.ACTION_REMOTE_INTENT)) {
@@ -54,7 +57,8 @@ public class MasterClearReceiver extends BroadcastReceiver {
@Override
public void run() {
try {
- RecoverySystem.rebootWipeUserData(context, shutdown, reason);
+ boolean wipeMedia = intent.getBooleanExtra(EXTRA_WIPE_MEDIA, false);
+ RecoverySystem.rebootWipeUserData(context, shutdown, reason, wipeMedia);
Log.wtf(TAG, "Still running after master clear?!");
} catch (IOException e) {
Slog.e(TAG, "Can't perform master clear/factory reset", e);
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index ecfd042..95c6c0e 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -838,7 +838,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
timestampNanos = SystemClock.elapsedRealtimeNanos();
}
boolean isActive = cooked[2].equals("active");
- notifyInterfaceClassActivity(Integer.parseInt(cooked[3]),
+ notifyInterfaceClassActivity(cooked[3] == null ? 0 : Integer.parseInt(cooked[3]),
isActive ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
: DataConnectionRealTimeInfo.DC_POWER_STATE_LOW, timestampNanos, false);
return true;
diff --git a/services/core/java/com/android/server/PermissionDialog.java b/services/core/java/com/android/server/PermissionDialog.java
new file mode 100644
index 0000000..0f337b8
--- /dev/null
+++ b/services/core/java/com/android/server/PermissionDialog.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright (C) 2006 The Android Open Source 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 com.android.server;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+public class PermissionDialog extends BasePermissionDialog {
+ private final static String TAG = "PermissionDialog";
+
+ private final AppOpsService mService;
+ private final String mPackageName;
+ private final int mCode;
+ private View mView;
+ private CheckBox mChoice;
+ private int mUid;
+ final CharSequence[] mOpLabels;
+ private Context mContext;
+
+ // Event 'what' codes
+ static final int ACTION_ALLOWED = 0x2;
+ static final int ACTION_IGNORED = 0x4;
+ static final int ACTION_IGNORED_TIMEOUT = 0x8;
+
+ // 15s timeout, then we automatically dismiss the permission
+ // dialog. Otherwise, it may cause watchdog timeout sometimes.
+ static final long DISMISS_TIMEOUT = 1000 * 15 * 1;
+
+ public PermissionDialog(Context context, AppOpsService service,
+ int code, int uid, String packageName) {
+ super(context);
+
+ mContext = context;
+ Resources res = context.getResources();
+
+ mService = service;
+ mCode = code;
+ mPackageName = packageName;
+ mUid = uid;
+ mOpLabels = res.getTextArray(
+ com.android.internal.R.array.app_ops_labels);
+
+ setCancelable(false);
+
+ setButton(DialogInterface.BUTTON_POSITIVE,
+ res.getString(com.android.internal.R.string.allow), mHandler.obtainMessage(ACTION_ALLOWED));
+
+ setButton(DialogInterface.BUTTON_NEGATIVE,
+ res.getString(com.android.internal.R.string.deny), mHandler.obtainMessage(ACTION_IGNORED));
+
+ setTitle(res.getString(com.android.internal.R.string.privacy_guard_dialog_title));
+ WindowManager.LayoutParams attrs = getWindow().getAttributes();
+ attrs.setTitle("Permission info: " + getAppName(mPackageName));
+ attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR
+ | WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ getWindow().setAttributes(attrs);
+
+ mView = getLayoutInflater().inflate(
+ com.android.internal.R.layout.permission_confirmation_dialog,
+ null);
+ TextView tv = (TextView) mView.findViewById(
+ com.android.internal.R.id.permission_text);
+ mChoice = (CheckBox) mView.findViewById(
+ com.android.internal.R.id.permission_remember_choice_checkbox);
+ String name = getAppName(mPackageName);
+ if(name == null)
+ name = mPackageName;
+ tv.setText(mContext.getString(com.android.internal.R.string.privacy_guard_dialog_summary,
+ name, mOpLabels[mCode]));
+ setView(mView);
+
+ // After the timeout, pretend the user clicked the quit button
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(ACTION_IGNORED_TIMEOUT), DISMISS_TIMEOUT);
+ }
+
+ private String getAppName(String packageName) {
+ ApplicationInfo appInfo = null;
+ PackageManager pm = mContext.getPackageManager();
+ try {
+ appInfo = pm.getApplicationInfo(packageName,
+ PackageManager.GET_DISABLED_COMPONENTS
+ | PackageManager.GET_UNINSTALLED_PACKAGES);
+ } catch (final NameNotFoundException e) {
+ return null;
+ }
+ if(appInfo != null) {
+ return (String)pm.getApplicationLabel(appInfo);
+ }
+ return null;
+ }
+
+ private final Handler mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ int mode;
+ boolean remember = mChoice.isChecked();
+ switch(msg.what) {
+ case ACTION_ALLOWED:
+ mode = AppOpsManager.MODE_ALLOWED;
+ break;
+ case ACTION_IGNORED:
+ mode = AppOpsManager.MODE_IGNORED;
+ break;
+ default:
+ mode = AppOpsManager.MODE_IGNORED;
+ remember = false;
+ }
+ mService.notifyOperation(mCode, mUid, mPackageName, mode,
+ remember);
+ dismiss();
+ }
+ };
+}
diff --git a/services/core/java/com/android/server/PermissionDialogReqQueue.java b/services/core/java/com/android/server/PermissionDialogReqQueue.java
new file mode 100644
index 0000000..ee94427
--- /dev/null
+++ b/services/core/java/com/android/server/PermissionDialogReqQueue.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright (C) 2006 The Android Open Source 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 com.android.server;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PermissionDialogReqQueue {
+ public PermissionDialog getDialog() {
+ return mDialog;
+ }
+
+ public void setDialog(PermissionDialog mDialog) {
+ this.mDialog = mDialog;
+ }
+
+ public final static class PermissionDialogReq {
+ public void set(int res) {
+ synchronized (this) {
+ mHasResult = true;
+ mResult = res;
+ notifyAll();
+ }
+ }
+
+ public int get() {
+ synchronized (this) {
+ while (!mHasResult) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ return mResult;
+ }
+
+ boolean mHasResult = false;
+ int mResult;
+ }
+
+ private PermissionDialog mDialog;
+ private List<PermissionDialogReq> resultList;
+
+ public PermissionDialogReqQueue() {
+ mDialog = null;
+ resultList = new ArrayList<PermissionDialogReq>();
+ }
+
+ public void register(PermissionDialogReq res) {
+ synchronized (this) {
+ resultList.add(res);
+ }
+ }
+
+ public void notifyAll(int mode) {
+ synchronized (this) {
+ while (resultList.size() != 0) {
+ PermissionDialogReq res = resultList.get(0);
+ res.set(mode);
+ resultList.remove(0);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index cd61347..2d94370 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -18,6 +18,7 @@ package com.android.server;
import android.app.ActivityManager;
import android.content.pm.FeatureInfo;
+import android.content.pm.Signature;
import android.os.*;
import android.os.Process;
import android.util.ArrayMap;
@@ -99,6 +100,9 @@ public class SystemConfig {
// URL-handling state upon factory reset.
final ArraySet<String> mLinkedApps = new ArraySet<>();
+ final ArrayMap<Signature, ArraySet<String>> mSignatureAllowances
+ = new ArrayMap<Signature, ArraySet<String>>();
+
public static SystemConfig getInstance() {
synchronized (SystemConfig.class) {
if (sInstance == null) {
@@ -144,6 +148,10 @@ public class SystemConfig {
return mLinkedApps;
}
+ public ArrayMap<Signature, ArraySet<String>> getSignatureAllowances() {
+ return mSignatureAllowances;
+ }
+
SystemConfig() {
// Read configuration from system
readPermissions(Environment.buildPath(
@@ -290,6 +298,43 @@ public class SystemConfig {
perms.add(perm);
XmlUtils.skipCurrentTag(parser);
+ } else if ("allow-permission".equals(name)) {
+ String perm = parser.getAttributeValue(null, "name");
+ if (perm == null) {
+ Slog.w(TAG,
+ "<allow-permission> without name at "
+ + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ String signature = parser.getAttributeValue(null, "signature");
+ if (signature == null) {
+ Slog.w(TAG,
+ "<allow-permission> without signature at "
+ + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ Signature sig = null;
+ try {
+ sig = new Signature(signature);
+ } catch (IllegalArgumentException e) {
+ // sig will be null so we will log it below
+ }
+ if (sig != null) {
+ ArraySet<String> perms = mSignatureAllowances.get(sig);
+ if (perms == null) {
+ perms = new ArraySet<String>();
+ mSignatureAllowances.put(sig, perms);
+ }
+ perms.add(perm);
+ } else {
+ Slog.w(TAG,
+ "<allow-permission> with bad signature at "
+ + parser.getPositionDescription());
+ }
+ XmlUtils.skipCurrentTag(parser);
+
} else if ("library".equals(name) && !onlyFeatures) {
String lname = parser.getAttributeValue(null, "name");
String lfile = parser.getAttributeValue(null, "file");
diff --git a/services/core/java/com/android/server/ThemeService.java b/services/core/java/com/android/server/ThemeService.java
new file mode 100644
index 0000000..2553961
--- /dev/null
+++ b/services/core/java/com/android/server/ThemeService.java
@@ -0,0 +1,1244 @@
+/*
+ * Copyright (C) 2014 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 com.android.server;
+
+import android.Manifest;
+import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.WallpaperManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ThemeUtils;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.IThemeProcessingListener;
+import android.content.res.ThemeChangeRequest;
+import android.content.res.ThemeConfig;
+import android.content.res.IThemeChangeListener;
+import android.content.res.IThemeService;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.media.RingtoneManager;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.provider.ThemesContract;
+import android.provider.ThemesContract.MixnMatchColumns;
+import android.provider.ThemesContract.ThemesColumns;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.R;
+import com.android.internal.util.cm.ImageUtils;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import static android.content.pm.ThemeUtils.SYSTEM_THEME_PATH;
+import static android.content.pm.ThemeUtils.THEME_BOOTANIMATION_PATH;
+import static android.content.res.ThemeConfig.SYSTEM_DEFAULT;
+
+import java.util.List;
+
+/**
+ * {@hide}
+ */
+public class ThemeService extends IThemeService.Stub {
+ private static final String TAG = ThemeService.class.getName();
+
+ private static final boolean DEBUG = false;
+
+ private static final String GOOGLE_SETUPWIZARD_PACKAGE = "com.google.android.setupwizard";
+ private static final String CM_SETUPWIZARD_PACKAGE = "com.cyanogenmod.account";
+
+ private static final long MAX_ICON_CACHE_SIZE = 33554432L; // 32MB
+ private static final long PURGED_ICON_CACHE_SIZE = 25165824L; // 24 MB
+
+ // Defines a min and max compatible api level for themes on this system.
+ private static final int MIN_COMPATIBLE_VERSION = 21;
+
+ private HandlerThread mWorker;
+ private ThemeWorkerHandler mHandler;
+ private ResourceProcessingHandler mResourceProcessingHandler;
+ private Context mContext;
+ private PackageManager mPM;
+ private int mProgress;
+ private boolean mWallpaperChangedByUs = false;
+ private long mIconCacheSize = 0L;
+ private int mCurrentUserId = UserHandle.USER_OWNER;
+
+ private boolean mIsThemeApplying = false;
+
+ private final RemoteCallbackList<IThemeChangeListener> mClients =
+ new RemoteCallbackList<IThemeChangeListener>();
+
+ private final RemoteCallbackList<IThemeProcessingListener> mProcessingListeners =
+ new RemoteCallbackList<IThemeProcessingListener>();
+
+ final private ArrayList<String> mThemesToProcessQueue = new ArrayList<String>(0);
+
+ private class ThemeWorkerHandler extends Handler {
+ private static final int MESSAGE_CHANGE_THEME = 1;
+ private static final int MESSAGE_APPLY_DEFAULT_THEME = 2;
+ private static final int MESSAGE_REBUILD_RESOURCE_CACHE = 3;
+
+ public ThemeWorkerHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_CHANGE_THEME:
+ final ThemeChangeRequest request = (ThemeChangeRequest) msg.obj;
+ doApplyTheme(request, msg.arg1 == 1);
+ break;
+ case MESSAGE_APPLY_DEFAULT_THEME:
+ doApplyDefaultTheme();
+ break;
+ case MESSAGE_REBUILD_RESOURCE_CACHE:
+ doRebuildResourceCache();
+ break;
+ default:
+ Log.w(TAG, "Unknown message " + msg.what);
+ break;
+ }
+ }
+ }
+
+ private class ResourceProcessingHandler extends Handler {
+ private static final int MESSAGE_QUEUE_THEME_FOR_PROCESSING = 3;
+ private static final int MESSAGE_DEQUEUE_AND_PROCESS_THEME = 4;
+
+ public ResourceProcessingHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_QUEUE_THEME_FOR_PROCESSING:
+ String pkgName = (String) msg.obj;
+ synchronized (mThemesToProcessQueue) {
+ if (!mThemesToProcessQueue.contains(pkgName)) {
+ if (DEBUG) Log.d(TAG, "Adding " + pkgName + " for processing");
+ mThemesToProcessQueue.add(pkgName);
+ if (mThemesToProcessQueue.size() == 1) {
+ this.sendEmptyMessage(MESSAGE_DEQUEUE_AND_PROCESS_THEME);
+ }
+ }
+ }
+ break;
+ case MESSAGE_DEQUEUE_AND_PROCESS_THEME:
+ synchronized (mThemesToProcessQueue) {
+ pkgName = mThemesToProcessQueue.get(0);
+ }
+ if (pkgName != null) {
+ if (DEBUG) Log.d(TAG, "Processing " + pkgName);
+ String name;
+ try {
+ PackageInfo pi = mPM.getPackageInfo(pkgName, 0);
+ name = getThemeName(pi);
+ } catch (PackageManager.NameNotFoundException e) {
+ name = null;
+ }
+
+ int result = mPM.processThemeResources(pkgName);
+ if (result < 0) {
+ postFailedThemeInstallNotification(name != null ? name : pkgName);
+ }
+ sendThemeResourcesCachedBroadcast(pkgName, result);
+
+ synchronized (mThemesToProcessQueue) {
+ mThemesToProcessQueue.remove(0);
+ if (mThemesToProcessQueue.size() > 0 &&
+ !hasMessages(MESSAGE_DEQUEUE_AND_PROCESS_THEME)) {
+ this.sendEmptyMessage(MESSAGE_DEQUEUE_AND_PROCESS_THEME);
+ }
+ }
+ postFinishedProcessing(pkgName);
+ }
+ break;
+ default:
+ Log.w(TAG, "Unknown message " + msg.what);
+ break;
+ }
+ }
+ }
+
+ public ThemeService(Context context) {
+ super();
+ mContext = context;
+ mWorker = new HandlerThread("ThemeServiceWorker", Process.THREAD_PRIORITY_BACKGROUND);
+ mWorker.start();
+ mHandler = new ThemeWorkerHandler(mWorker.getLooper());
+ Log.i(TAG, "Spawned worker thread");
+
+ HandlerThread processingThread = new HandlerThread("ResourceProcessingThread",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ processingThread.start();
+ mResourceProcessingHandler =
+ new ResourceProcessingHandler(processingThread.getLooper());
+
+ // create the theme directory if it does not exist
+ ThemeUtils.createThemeDirIfNotExists();
+ ThemeUtils.createFontDirIfNotExists();
+ ThemeUtils.createAlarmDirIfNotExists();
+ ThemeUtils.createNotificationDirIfNotExists();
+ ThemeUtils.createRingtoneDirIfNotExists();
+ ThemeUtils.createIconCacheDirIfNotExists();
+ }
+
+ public void systemRunning() {
+ // listen for wallpaper changes
+ IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
+ mContext.registerReceiver(mWallpaperChangeReceiver, filter);
+
+ filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
+ mContext.registerReceiver(mUserChangeReceiver, filter);
+
+ mPM = mContext.getPackageManager();
+
+ processInstalledThemes();
+
+ if (!isThemeApiUpToDate()) {
+ Log.d(TAG, "The system has been upgraded to a theme new api, " +
+ "checking if currently set theme is compatible");
+ removeObsoleteThemeOverlayIfExists();
+ updateThemeApi();
+ }
+ }
+
+ private void removeObsoleteThemeOverlayIfExists() {
+ // Get the current overlay theme so we can see it it's overlay should be unapplied
+ final IActivityManager am = ActivityManagerNative.getDefault();
+ ThemeConfig config = null;
+ try {
+ if (am != null) {
+ config = am.getConfiguration().themeConfig;
+ } else {
+ Log.e(TAG, "ActivityManager getDefault() " +
+ "returned null, cannot remove obsolete theme");
+ }
+ } catch(RemoteException e) {
+ Log.e(TAG, "Failed to get the theme config ", e);
+ }
+ if (config == null) return; // No need to unapply a theme if one isn't set
+
+ // Populate the currentTheme map for the components we care about, we'll look
+ // at the compatibility of each pkg below.
+ HashMap<String, String> currentThemeMap = new HashMap<String, String>();
+ currentThemeMap.put(ThemesColumns.MODIFIES_STATUS_BAR, config.getOverlayForStatusBar());
+ currentThemeMap.put(ThemesColumns.MODIFIES_NAVIGATION_BAR,
+ config.getOverlayForNavBar());
+ currentThemeMap.put(ThemesColumns.MODIFIES_OVERLAYS, config.getOverlayPkgName());
+
+ // Look at each component's theme (that we care about at least) and check compatibility
+ // of the pkg with the system. If it is not compatible then we will add it to a theme
+ // change request.
+ Map<String, String> defaults = ThemeUtils.getDefaultComponents(mContext);
+ ThemeChangeRequest.Builder builder = new ThemeChangeRequest.Builder();
+ for(Map.Entry<String, String> entry : currentThemeMap.entrySet()) {
+ String component = entry.getKey();
+ String pkgName = entry.getValue();
+ String defaultPkg = defaults.get(component);
+
+ // Check that the default overlay theme is not currently set
+ if (defaultPkg.equals(pkgName)) {
+ Log.d(TAG, "Current overlay theme is same as default. " +
+ "Not doing anything for " + component);
+ continue;
+ }
+
+ // No need to unapply a system theme since it is always compatible
+ if (ThemeConfig.SYSTEM_DEFAULT.equals(pkgName)) {
+ Log.d(TAG, "Current overlay theme for "
+ + component + " was system. no need to unapply");
+ continue;
+ }
+
+ if (!isThemeCompatibleWithUpgradedApi(pkgName)) {
+ Log.d(TAG, pkgName + "is incompatible with latest theme api for component " +
+ component + ", Applying " + defaultPkg);
+ builder.setComponent(component, pkgName);
+ }
+ }
+
+ // Now actually unapply the incompatible themes
+ ThemeChangeRequest request = builder.build();
+ if (!request.getThemeComponentsMap().isEmpty()) {
+ try {
+ requestThemeChange(request, true);
+ } catch(RemoteException e) {
+ // This cannot happen
+ }
+ } else {
+ Log.d(TAG, "Current theme is compatible with the system. Not unapplying anything");
+ }
+ }
+
+ private boolean isThemeCompatibleWithUpgradedApi(String pkgName) {
+ // Note this function does not cover the case of a downgrade. That case is out of scope and
+ // would require predicting whether the future API levels will be compatible or not.
+ boolean compatible = false;
+ try {
+ PackageInfo pi = mPM.getPackageInfo(pkgName, 0);
+ Log.d(TAG, "Comparing theme target: " + pi.applicationInfo.targetSdkVersion +
+ "to " + android.os.Build.VERSION.SDK_INT);
+ compatible = pi.applicationInfo.targetSdkVersion >= MIN_COMPATIBLE_VERSION;
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Unable to get package info for " + pkgName, e);
+ }
+ return compatible;
+ }
+
+ private boolean isThemeApiUpToDate() {
+ // We can't be 100% sure its an upgrade. If the field is undefined it
+ // could have been a factory reset.
+ final ContentResolver resolver = mContext.getContentResolver();
+ int recordedApiLevel = android.os.Build.VERSION.SDK_INT;
+ try {
+ recordedApiLevel = Settings.Secure.getInt(resolver,
+ Settings.Secure.THEME_PREV_BOOT_API_LEVEL);
+ } catch (SettingNotFoundException e) {
+ recordedApiLevel = -1;
+ Log.d(TAG, "Previous api level not found. First time booting?");
+ }
+ Log.d(TAG, "Prev api level was: " + recordedApiLevel
+ + ", api is now: " + android.os.Build.VERSION.SDK_INT);
+
+ return recordedApiLevel == android.os.Build.VERSION.SDK_INT;
+ }
+
+ private void updateThemeApi() {
+ final ContentResolver resolver = mContext.getContentResolver();
+ boolean success = Settings.Secure.putInt(resolver,
+ Settings.Secure.THEME_PREV_BOOT_API_LEVEL, android.os.Build.VERSION.SDK_INT);
+ if (!success) {
+ Log.e(TAG, "Unable to store latest API level to secure settings");
+ }
+ }
+
+ private void doApplyTheme(ThemeChangeRequest request, boolean removePerAppTheme) {
+ synchronized(this) {
+ mProgress = 0;
+ }
+
+ if (request == null || request.getNumChangesRequested() == 0) {
+ postFinish(true, request, 0);
+ return;
+ }
+ mIsThemeApplying = true;
+ long updateTime = System.currentTimeMillis();
+
+ incrementProgress(5);
+
+ // TODO: provide progress updates that reflect the time needed for each component
+ final int progressIncrement = 75 / request.getNumChangesRequested();
+
+ if (request.getIconsThemePackageName() != null) {
+ updateIcons(request.getIconsThemePackageName());
+ incrementProgress(progressIncrement);
+ }
+
+ if (request.getWallpaperThemePackageName() != null) {
+ if (updateWallpaper(request.getWallpaperThemePackageName(),
+ request.getWallpaperId())) {
+ mWallpaperChangedByUs = true;
+ }
+ incrementProgress(progressIncrement);
+ }
+
+ if (request.getLockWallpaperThemePackageName() != null) {
+ updateLockscreen(request.getLockWallpaperThemePackageName());
+ incrementProgress(progressIncrement);
+ }
+
+ Environment.setUserRequired(false);
+ if (request.getNotificationThemePackageName() != null) {
+ updateNotifications(request.getNotificationThemePackageName());
+ incrementProgress(progressIncrement);
+ }
+
+ if (request.getAlarmThemePackageName() != null) {
+ updateAlarms(request.getAlarmThemePackageName());
+ incrementProgress(progressIncrement);
+ }
+
+ if (request.getRingtoneThemePackageName() != null) {
+ updateRingtones(request.getRingtoneThemePackageName());
+ incrementProgress(progressIncrement);
+ }
+ Environment.setUserRequired(true);
+
+ if (request.getBootanimationThemePackageName() != null) {
+ updateBootAnim(request.getBootanimationThemePackageName());
+ incrementProgress(progressIncrement);
+ }
+
+ if (request.getFontThemePackageName() != null) {
+ updateFonts(request.getFontThemePackageName());
+ incrementProgress(progressIncrement);
+ }
+
+ try {
+ updateProvider(request, updateTime);
+ } catch(IllegalArgumentException e) {
+ // Safeguard against provider not being ready yet.
+ Log.e(TAG, "Not updating the theme provider since it is unavailable");
+ }
+
+ if (shouldUpdateConfiguration(request)) {
+ updateConfiguration(request, removePerAppTheme);
+ }
+
+ killLaunchers(request);
+
+ postFinish(true, request, updateTime);
+ mIsThemeApplying = false;
+ }
+
+ private void doApplyDefaultTheme() {
+ final ContentResolver resolver = mContext.getContentResolver();
+ final String defaultThemePkg = Settings.Secure.getString(resolver,
+ Settings.Secure.DEFAULT_THEME_PACKAGE);
+ if (!TextUtils.isEmpty(defaultThemePkg)) {
+ String defaultThemeComponents = Settings.Secure.getString(resolver,
+ Settings.Secure.DEFAULT_THEME_COMPONENTS);
+ List<String> components;
+ if (TextUtils.isEmpty(defaultThemeComponents)) {
+ components = ThemeUtils.getAllComponents();
+ } else {
+ components = new ArrayList<String>(
+ Arrays.asList(defaultThemeComponents.split("\\|")));
+ }
+ ThemeChangeRequest.Builder builder = new ThemeChangeRequest.Builder();
+ for (String component : components) {
+ builder.setComponent(component, defaultThemePkg);
+ }
+ try {
+ requestThemeChange(builder.build(), true);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to set default theme", e);
+ }
+ }
+ }
+
+ private void doRebuildResourceCache() {
+ FileUtils.deleteContents(new File(ThemeUtils.RESOURCE_CACHE_DIR));
+ processInstalledThemes();
+ }
+
+ private void updateProvider(ThemeChangeRequest request, long updateTime) {
+ ContentValues values = new ContentValues();
+ values.put(MixnMatchColumns.COL_UPDATE_TIME, updateTime);
+ Map<String, String> componentMap = request.getThemeComponentsMap();
+ for (String component : componentMap.keySet()) {
+ values.put(ThemesContract.MixnMatchColumns.COL_VALUE, componentMap.get(component));
+ String where = ThemesContract.MixnMatchColumns.COL_KEY + "=?";
+ String[] selectionArgs = { MixnMatchColumns.componentToMixNMatchKey(component) };
+ if (selectionArgs[0] == null) {
+ continue; // No equivalence between mixnmatch and theme
+ }
+
+ // Add component ID for multiwallpaper
+ if (ThemesColumns.MODIFIES_LAUNCHER.equals(component)) {
+ values.put(MixnMatchColumns.COL_COMPONENT_ID, request.getWallpaperId());
+ }
+
+ mContext.getContentResolver().update(MixnMatchColumns.CONTENT_URI, values, where,
+ selectionArgs);
+ }
+ }
+
+ private boolean updateIcons(String pkgName) {
+ ThemeUtils.clearIconCache();
+ try {
+ if (pkgName.equals(SYSTEM_DEFAULT)) {
+ mPM.updateIconMaps(null);
+ } else {
+ mPM.updateIconMaps(pkgName);
+ }
+ } catch (Exception e) {
+ Log.w(TAG, "Changing icons failed", e);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean updateFonts(String pkgName) {
+ //Clear the font dir
+ FileUtils.deleteContents(new File(ThemeUtils.SYSTEM_THEME_FONT_PATH));
+
+ if (!pkgName.equals(SYSTEM_DEFAULT)) {
+ //Get Font Assets
+ Context themeCtx;
+ String[] assetList;
+ try {
+ themeCtx = mContext.createPackageContext(pkgName, Context.CONTEXT_IGNORE_SECURITY);
+ AssetManager assetManager = themeCtx.getAssets();
+ assetList = assetManager.list("fonts");
+ } catch (Exception e) {
+ Log.e(TAG, "There was an error getting assets for pkg " + pkgName, e);
+ return false;
+ }
+ if (assetList == null || assetList.length == 0) {
+ Log.e(TAG, "Could not find any font assets");
+ return false;
+ }
+
+ //Copy font assets to font dir
+ for(String asset : assetList) {
+ InputStream is = null;
+ OutputStream os = null;
+ try {
+ is = ThemeUtils.getInputStreamFromAsset(themeCtx,
+ "file:///android_asset/fonts/" + asset);
+ File outFile = new File(ThemeUtils.SYSTEM_THEME_FONT_PATH, asset);
+ FileUtils.copyToFile(is, outFile);
+ FileUtils.setPermissions(outFile,
+ FileUtils.S_IRWXU|FileUtils.S_IRGRP|FileUtils.S_IRWXO, -1, -1);
+ } catch (Exception e) {
+ Log.e(TAG, "There was an error installing the new fonts for pkg " + pkgName, e);
+ return false;
+ } finally {
+ ThemeUtils.closeQuietly(is);
+ ThemeUtils.closeQuietly(os);
+ }
+ }
+ }
+
+ //Notify zygote that themes need a refresh
+ SystemProperties.set("sys.refresh_theme", "1");
+ return true;
+ }
+
+ private boolean updateBootAnim(String pkgName) {
+ if (SYSTEM_DEFAULT.equals(pkgName)) {
+ clearBootAnimation();
+ return true;
+ }
+
+ try {
+ final ApplicationInfo ai = mPM.getApplicationInfo(pkgName, 0);
+ applyBootAnimation(ai.sourceDir);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Changing boot animation failed", e);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean updateAlarms(String pkgName) {
+ return updateAudible(ThemeUtils.SYSTEM_THEME_ALARM_PATH, "alarms",
+ RingtoneManager.TYPE_ALARM, pkgName);
+ }
+
+ private boolean updateNotifications(String pkgName) {
+ return updateAudible(ThemeUtils.SYSTEM_THEME_NOTIFICATION_PATH, "notifications",
+ RingtoneManager.TYPE_NOTIFICATION, pkgName);
+ }
+
+ private boolean updateRingtones(String pkgName) {
+ return updateAudible(ThemeUtils.SYSTEM_THEME_RINGTONE_PATH, "ringtones",
+ RingtoneManager.TYPE_RINGTONE, pkgName);
+ }
+
+ private boolean updateAudible(String dirPath, String assetPath, int type, String pkgName) {
+ //Clear the dir
+ ThemeUtils.clearAudibles(mContext, dirPath);
+ if (pkgName.equals(SYSTEM_DEFAULT)) {
+ if (!ThemeUtils.setDefaultAudible(mContext, type)) {
+ Log.e(TAG, "There was an error installing the default audio file");
+ return false;
+ }
+ return true;
+ }
+
+ PackageInfo pi = null;
+ try {
+ pi = mPM.getPackageInfo(pkgName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Unable to update audible " + dirPath, e);
+ return false;
+ }
+
+ //Get theme Assets
+ Context themeCtx;
+ String[] assetList;
+ try {
+ themeCtx = mContext.createPackageContext(pkgName, Context.CONTEXT_IGNORE_SECURITY);
+ AssetManager assetManager = themeCtx.getAssets();
+ assetList = assetManager.list(assetPath);
+ } catch (Exception e) {
+ Log.e(TAG, "There was an error getting assets for pkg " + pkgName, e);
+ return false;
+ }
+ if (assetList == null || assetList.length == 0) {
+ Log.e(TAG, "Could not find any audio assets");
+ return false;
+ }
+
+ // TODO: right now we just load the first file but this will need to be changed
+ // in the future if multiple audio files are supported.
+ final String asset = assetList[0];
+ if (!ThemeUtils.isValidAudible(asset)) return false;
+
+ InputStream is = null;
+ OutputStream os = null;
+ try {
+ is = ThemeUtils.getInputStreamFromAsset(themeCtx, "file:///android_asset/"
+ + assetPath + File.separator + asset);
+ File outFile = new File(dirPath, asset);
+ FileUtils.copyToFile(is, outFile);
+ FileUtils.setPermissions(outFile,
+ FileUtils.S_IRWXU|FileUtils.S_IRGRP|FileUtils.S_IRWXO,-1, -1);
+ ThemeUtils.setAudible(mContext, outFile, type, pi.themeInfo.name);
+ } catch (Exception e) {
+ Log.e(TAG, "There was an error installing the new audio file for pkg " + pkgName, e);
+ return false;
+ } finally {
+ ThemeUtils.closeQuietly(is);
+ ThemeUtils.closeQuietly(os);
+ }
+ return true;
+ }
+
+ private boolean updateLockscreen(String pkgName) {
+ boolean success;
+ success = setCustomLockScreenWallpaper(pkgName);
+
+ // TODO: uncomment once Keyguard wallpaper is re-implemented
+ /*
+ if (success) {
+ mContext.sendBroadcastAsUser(new Intent(Intent.ACTION_KEYGUARD_WALLPAPER_CHANGED),
+ UserHandle.ALL);
+ }
+ */
+ return success;
+ }
+
+ private boolean setCustomLockScreenWallpaper(String pkgName) {
+ // TODO: uncomment once Keyguard wallpaper is re-implemented
+ /*
+ WallpaperManager wm = WallpaperManager.getInstance(mContext);
+ try {
+ if (SYSTEM_DEFAULT.equals(pkgName) || TextUtils.isEmpty(pkgName)) {
+ wm.clearKeyguardWallpaper();
+ } else {
+ InputStream in = ImageUtils.getCroppedKeyguardStream(pkgName, mContext);
+ if (in != null) {
+ wm.setKeyguardStream(in);
+ ThemeUtils.closeQuietly(in);
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "There was an error setting lockscreen wp for pkg " + pkgName, e);
+ return false;
+ }
+ */
+ return true;
+ }
+
+ private boolean updateWallpaper(String pkgName, long id) {
+ WallpaperManager wm = WallpaperManager.getInstance(mContext);
+ if (SYSTEM_DEFAULT.equals(pkgName)) {
+ try {
+ wm.clear();
+ } catch (IOException e) {
+ return false;
+ }
+ } else if (TextUtils.isEmpty(pkgName)) {
+ try {
+ wm.clear(false);
+ } catch (IOException e) {
+ return false;
+ }
+ } else {
+ InputStream in = null;
+ try {
+ in = ImageUtils.getCroppedWallpaperStream(pkgName, id, mContext);
+ if (in != null)
+ wm.setStream(in);
+ } catch (Exception e) {
+ return false;
+ } finally {
+ ThemeUtils.closeQuietly(in);
+ }
+ }
+ return true;
+ }
+
+ private boolean updateConfiguration(ThemeChangeRequest request,
+ boolean removePerAppThemes) {
+ final IActivityManager am = ActivityManagerNative.getDefault();
+ if (am != null) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ Configuration config = am.getConfiguration();
+ ThemeConfig.Builder themeBuilder = createBuilderFrom(config, request, null,
+ removePerAppThemes);
+ ThemeConfig newConfig = themeBuilder.build();
+
+ config.themeConfig = newConfig;
+ am.updateConfiguration(config);
+ } catch (RemoteException e) {
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ return true;
+ }
+
+ private boolean updateConfiguration(ThemeConfig themeConfig) {
+ final IActivityManager am = ActivityManagerNative.getDefault();
+ if (am != null) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ Configuration config = am.getConfiguration();
+
+ config.themeConfig = themeConfig;
+ am.updateConfiguration(config);
+ } catch (RemoteException e) {
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ return true;
+ }
+
+ private boolean shouldUpdateConfiguration(ThemeChangeRequest request) {
+ return request.getOverlayThemePackageName() != null ||
+ request.getFontThemePackageName() != null ||
+ request.getIconsThemePackageName() != null ||
+ request.getStatusBarThemePackageName() != null ||
+ request.getNavBarThemePackageName() != null ||
+ request.getPerAppOverlays().size() > 0;
+ }
+
+ private static ThemeConfig.Builder createBuilderFrom(Configuration config,
+ ThemeChangeRequest request, String pkgName, boolean removePerAppThemes) {
+ ThemeConfig.Builder builder = new ThemeConfig.Builder(config.themeConfig);
+
+ if (removePerAppThemes) removePerAppThemesFromConfig(builder, config.themeConfig);
+
+ if (request.getIconsThemePackageName() != null) {
+ builder.defaultIcon(pkgName == null ? request.getIconsThemePackageName() : pkgName);
+ }
+
+ if (request.getOverlayThemePackageName() != null) {
+ builder.defaultOverlay(pkgName == null ?
+ request.getOverlayThemePackageName() : pkgName);
+ }
+
+ if (request.getFontThemePackageName() != null) {
+ builder.defaultFont(pkgName == null ? request.getFontThemePackageName() : pkgName);
+ }
+
+ if (request.getStatusBarThemePackageName() != null) {
+ builder.overlay(ThemeConfig.SYSTEMUI_STATUS_BAR_PKG, pkgName == null ?
+ request.getStatusBarThemePackageName() : pkgName);
+ }
+
+ if (request.getNavBarThemePackageName() != null) {
+ builder.overlay(ThemeConfig.SYSTEMUI_NAVBAR_PKG, pkgName == null ?
+ request.getNavBarThemePackageName() : pkgName);
+ }
+
+ // check for any per app overlays being applied
+ Map<String, String> appOverlays = request.getPerAppOverlays();
+ for (String appPkgName : appOverlays.keySet()) {
+ if (appPkgName != null) {
+ builder.overlay(appPkgName, appOverlays.get(appPkgName));
+ }
+ }
+
+ builder.setLastThemeChangeRequestType(request.getReqeustType());
+
+ return builder;
+ }
+
+ private static void removePerAppThemesFromConfig(ThemeConfig.Builder builder,
+ ThemeConfig themeConfig) {
+ if (themeConfig != null) {
+ Map<String, ThemeConfig.AppTheme> themes = themeConfig.getAppThemes();
+ for (String appPkgName : themes.keySet()) {
+ if (ThemeUtils.isPerAppThemeComponent(appPkgName)) {
+ builder.overlay(appPkgName, null);
+ }
+ }
+ }
+ }
+
+ // Kill the current Home process, they tend to be evil and cache
+ // drawable references in all apps
+ private void killLaunchers(ThemeChangeRequest request) {
+ if (request.getOverlayThemePackageName() == null
+ && request.getIconsThemePackageName() == null) {
+ return;
+ }
+
+ final ActivityManager am =
+ (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+
+ Intent homeIntent = new Intent();
+ homeIntent.setAction(Intent.ACTION_MAIN);
+ homeIntent.addCategory(Intent.CATEGORY_HOME);
+
+ List<ResolveInfo> infos = mPM.queryIntentActivities(homeIntent, 0);
+ List<ResolveInfo> themeChangeInfos = mPM.queryBroadcastReceivers(
+ new Intent(ThemeUtils.ACTION_THEME_CHANGED), 0);
+ for(ResolveInfo info : infos) {
+ if (info.activityInfo != null && info.activityInfo.applicationInfo != null &&
+ !isSetupActivity(info) && !handlesThemeChanges(
+ info.activityInfo.applicationInfo.packageName, themeChangeInfos)) {
+ String pkgToStop = info.activityInfo.applicationInfo.packageName;
+ Log.d(TAG, "Force stopping " + pkgToStop + " for theme change");
+ try {
+ am.forceStopPackage(pkgToStop);
+ } catch(Exception e) {
+ Log.e(TAG, "Unable to force stop package, did you forget platform signature?",
+ e);
+ }
+ }
+ }
+ }
+
+ private boolean isSetupActivity(ResolveInfo info) {
+ return GOOGLE_SETUPWIZARD_PACKAGE.equals(info.activityInfo.packageName) ||
+ CM_SETUPWIZARD_PACKAGE.equals(info.activityInfo.packageName);
+ }
+
+ private boolean handlesThemeChanges(String pkgName, List<ResolveInfo> infos) {
+ if (infos != null && infos.size() > 0) {
+ for (ResolveInfo info : infos) {
+ if (info.activityInfo.applicationInfo.packageName.equals(pkgName)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private void postProgress() {
+ int N = mClients.beginBroadcast();
+ for(int i=0; i < N; i++) {
+ IThemeChangeListener listener = mClients.getBroadcastItem(0);
+ try {
+ listener.onProgress(mProgress);
+ } catch(RemoteException e) {
+ Log.w(TAG, "Unable to post progress to client listener", e);
+ }
+ }
+ mClients.finishBroadcast();
+ }
+
+ private void postFinish(boolean isSuccess, ThemeChangeRequest request, long updateTime) {
+ synchronized(this) {
+ mProgress = 0;
+ }
+
+ int N = mClients.beginBroadcast();
+ for(int i=0; i < N; i++) {
+ IThemeChangeListener listener = mClients.getBroadcastItem(0);
+ try {
+ listener.onFinish(isSuccess);
+ } catch(RemoteException e) {
+ Log.w(TAG, "Unable to post progress to client listener", e);
+ }
+ }
+ mClients.finishBroadcast();
+
+ // if successful, broadcast that the theme changed
+ if (isSuccess) {
+ broadcastThemeChange(request, updateTime);
+ }
+ }
+
+ private void postFinishedProcessing(String pkgName) {
+ int N = mProcessingListeners.beginBroadcast();
+ for(int i=0; i < N; i++) {
+ IThemeProcessingListener listener = mProcessingListeners.getBroadcastItem(0);
+ try {
+ listener.onFinishedProcessing(pkgName);
+ } catch(RemoteException e) {
+ Log.w(TAG, "Unable to post progress to listener", e);
+ }
+ }
+ mProcessingListeners.finishBroadcast();
+ }
+
+ private void broadcastThemeChange(ThemeChangeRequest request, long updateTime) {
+ Map<String, String> componentMap = request.getThemeComponentsMap();
+ if (componentMap == null || componentMap.size() == 0) return;
+
+ final Intent intent = new Intent(ThemeUtils.ACTION_THEME_CHANGED);
+ ArrayList componentsArrayList = new ArrayList(componentMap.keySet());
+ intent.putStringArrayListExtra(ThemeUtils.EXTRA_COMPONENTS, componentsArrayList);
+ intent.putExtra(ThemeUtils.EXTRA_REQUEST_TYPE, request.getReqeustType().ordinal());
+ intent.putExtra(ThemeUtils.EXTRA_UPDATE_TIME, updateTime);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ private void incrementProgress(int increment) {
+ synchronized(this) {
+ mProgress += increment;
+ if (mProgress > 100) mProgress = 100;
+ }
+ postProgress();
+ }
+
+ @Override
+ public void requestThemeChangeUpdates(IThemeChangeListener listener) throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.ACCESS_THEME_MANAGER, null);
+ mClients.register(listener);
+ }
+
+ @Override
+ public void removeUpdates(IThemeChangeListener listener) throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.ACCESS_THEME_MANAGER, null);
+ mClients.unregister(listener);
+ }
+
+ @Override
+ public void requestThemeChange(ThemeChangeRequest request, boolean removePerAppThemes)
+ throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.ACCESS_THEME_MANAGER, null);
+ Message msg;
+
+ /**
+ * Since the ThemeService handles compiling theme resource we need to make sure that any
+ * of the components we are trying to apply are either already processed or put to the
+ * front of the queue and handled before the theme change takes place.
+ *
+ * TODO: create a callback that can be sent to any ThemeChangeListeners to notify them that
+ * the theme will be applied once the processing is done.
+ */
+ synchronized (mThemesToProcessQueue) {
+ Map<String, String> componentMap = request.getThemeComponentsMap();
+ for (Object key : componentMap.keySet()) {
+ if (ThemesColumns.MODIFIES_OVERLAYS.equals(key) ||
+ ThemesColumns.MODIFIES_NAVIGATION_BAR.equals(key) ||
+ ThemesColumns.MODIFIES_STATUS_BAR.equals(key) ||
+ ThemesColumns.MODIFIES_ICONS.equals(key)) {
+ String pkgName = (String) componentMap.get(key);
+ if (mThemesToProcessQueue.indexOf(pkgName) > 0) {
+ mThemesToProcessQueue.remove(pkgName);
+ mThemesToProcessQueue.add(0, pkgName);
+ // We want to make sure these resources are taken care of first so
+ // send the dequeue message and place it in the front of the queue
+ msg = mResourceProcessingHandler.obtainMessage(
+ ResourceProcessingHandler.MESSAGE_DEQUEUE_AND_PROCESS_THEME);
+ mResourceProcessingHandler.sendMessageAtFrontOfQueue(msg);
+ }
+ }
+ }
+ }
+ msg = Message.obtain();
+ msg.what = ThemeWorkerHandler.MESSAGE_CHANGE_THEME;
+ msg.obj = request;
+ msg.arg1 = removePerAppThemes ? 1 : 0;
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void applyDefaultTheme() {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.ACCESS_THEME_MANAGER, null);
+ Message msg = Message.obtain();
+ msg.what = ThemeWorkerHandler.MESSAGE_APPLY_DEFAULT_THEME;
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public boolean isThemeApplying() throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_THEME_MANAGER, null);
+ return mIsThemeApplying;
+ }
+
+ @Override
+ public int getProgress() throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_THEME_MANAGER, null);
+ synchronized(this) {
+ return mProgress;
+ }
+ }
+
+ @Override
+ public boolean cacheComposedIcon(Bitmap icon, String fileName) throws RemoteException {
+ final long token = Binder.clearCallingIdentity();
+ boolean success;
+ FileOutputStream os;
+ final File cacheDir = new File(ThemeUtils.SYSTEM_THEME_ICON_CACHE_DIR);
+ if (cacheDir.listFiles().length == 0) {
+ mIconCacheSize = 0;
+ }
+ try {
+ File outFile = new File(cacheDir, fileName);
+ os = new FileOutputStream(outFile);
+ icon.compress(Bitmap.CompressFormat.PNG, 90, os);
+ os.close();
+ FileUtils.setPermissions(outFile,
+ FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IROTH,
+ -1, -1);
+ mIconCacheSize += outFile.length();
+ if (mIconCacheSize > MAX_ICON_CACHE_SIZE) {
+ purgeIconCache();
+ }
+ success = true;
+ } catch (Exception e) {
+ success = false;
+ Log.w(TAG, "Unable to cache icon " + fileName, e);
+ }
+ Binder.restoreCallingIdentity(token);
+ return success;
+ }
+
+ @Override
+ public boolean processThemeResources(String themePkgName) throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.ACCESS_THEME_MANAGER, null);
+ try {
+ mPM.getPackageInfo(themePkgName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ // Package doesn't exist so nothing to process
+ return false;
+ }
+ // Obtain a message and send it to the handler to process this theme
+ Message msg = mResourceProcessingHandler.obtainMessage(
+ ResourceProcessingHandler.MESSAGE_QUEUE_THEME_FOR_PROCESSING, 0, 0, themePkgName);
+ mResourceProcessingHandler.sendMessage(msg);
+ return true;
+ }
+
+ @Override
+ public boolean isThemeBeingProcessed(String themePkgName) throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.ACCESS_THEME_MANAGER, null);
+ synchronized (mThemesToProcessQueue) {
+ return mThemesToProcessQueue.contains(themePkgName);
+ }
+ }
+
+ @Override
+ public void registerThemeProcessingListener(IThemeProcessingListener listener)
+ throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.ACCESS_THEME_MANAGER, null);
+ mProcessingListeners.register(listener);
+ }
+
+ @Override
+ public void unregisterThemeProcessingListener(IThemeProcessingListener listener)
+ throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.ACCESS_THEME_MANAGER, null);
+ mProcessingListeners.unregister(listener);
+ }
+
+ @Override
+ public void rebuildResourceCache() throws RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.ACCESS_THEME_MANAGER, null);
+ mHandler.sendEmptyMessage(ThemeWorkerHandler.MESSAGE_REBUILD_RESOURCE_CACHE);
+ }
+
+ private void purgeIconCache() {
+ Log.d(TAG, "Purging icon cahe of size " + mIconCacheSize);
+ File cacheDir = new File(ThemeUtils.SYSTEM_THEME_ICON_CACHE_DIR);
+ File[] files = cacheDir.listFiles();
+ Arrays.sort(files, mOldestFilesFirstComparator);
+ for (File f : files) {
+ if (!f.isDirectory()) {
+ final long size = f.length();
+ if(f.delete()) mIconCacheSize -= size;
+ }
+ if (mIconCacheSize <= PURGED_ICON_CACHE_SIZE) break;
+ }
+ }
+
+ private boolean applyBootAnimation(String themePath) {
+ boolean success = false;
+ try {
+ ZipFile zip = new ZipFile(new File(themePath));
+ ZipEntry ze = zip.getEntry(THEME_BOOTANIMATION_PATH);
+ if (ze != null) {
+ clearBootAnimation();
+ BufferedInputStream is = new BufferedInputStream(zip.getInputStream(ze));
+ final String bootAnimationPath = SYSTEM_THEME_PATH + File.separator
+ + "bootanimation.zip";
+ ThemeUtils.copyAndScaleBootAnimation(mContext, is, bootAnimationPath);
+ FileUtils.setPermissions(bootAnimationPath,
+ FileUtils.S_IRWXU|FileUtils.S_IRGRP|FileUtils.S_IROTH, -1, -1);
+ }
+ zip.close();
+ success = true;
+ } catch (Exception e) {
+ Log.w(TAG, "Unable to load boot animation for " + themePath, e);
+ }
+
+ return success;
+ }
+
+ private void clearBootAnimation() {
+ File anim = new File(SYSTEM_THEME_PATH + File.separator + "bootanimation.zip");
+ if (anim.exists())
+ anim.delete();
+ }
+
+ private BroadcastReceiver mWallpaperChangeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!mWallpaperChangedByUs) {
+ // In case the mixnmatch table has a mods_launcher entry, we'll clear it
+ ThemeChangeRequest.Builder builder = new ThemeChangeRequest.Builder();
+ builder.setWallpaper("");
+ updateProvider(builder.build(), System.currentTimeMillis());
+ } else {
+ mWallpaperChangedByUs = false;
+ }
+ }
+ };
+
+ private BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (userHandle >= 0 && userHandle != mCurrentUserId) {
+ mCurrentUserId = userHandle;
+ ThemeConfig config = ThemeConfig.getBootThemeForUser(mContext.getContentResolver(),
+ userHandle);
+ if (DEBUG) {
+ Log.d(TAG,
+ "Changing theme for user " + userHandle + " to " + config.toString());
+ }
+ ThemeChangeRequest request = new ThemeChangeRequest.Builder(config).build();
+ try {
+ requestThemeChange(request, true);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to change theme for user change", e);
+ }
+ }
+ }
+ };
+
+ private Comparator<File> mOldestFilesFirstComparator = new Comparator<File>() {
+ @Override
+ public int compare(File lhs, File rhs) {
+ return (int) (lhs.lastModified() - rhs.lastModified());
+ }
+ };
+
+ private void processInstalledThemes() {
+ final String defaultTheme = ThemeUtils.getDefaultThemePackageName(mContext);
+ Message msg;
+ // Make sure the default theme is the first to get processed!
+ if (!ThemeConfig.SYSTEM_DEFAULT.equals(defaultTheme)) {
+ msg = mHandler.obtainMessage(
+ ResourceProcessingHandler.MESSAGE_QUEUE_THEME_FOR_PROCESSING,
+ 0, 0, defaultTheme);
+ mResourceProcessingHandler.sendMessage(msg);
+ }
+ // Iterate over all installed packages and queue up the ones that are themes or icon packs
+ List<PackageInfo> packages = mPM.getInstalledPackages(0);
+ for (PackageInfo info : packages) {
+ if (!defaultTheme.equals(info.packageName) &&
+ (info.isThemeApk || info.isLegacyIconPackApk)) {
+ msg = mHandler.obtainMessage(
+ ResourceProcessingHandler.MESSAGE_QUEUE_THEME_FOR_PROCESSING,
+ 0, 0, info.packageName);
+ mResourceProcessingHandler.sendMessage(msg);
+ }
+ }
+ }
+
+ private void sendThemeResourcesCachedBroadcast(String themePkgName, int resultCode) {
+ final Intent intent = new Intent(Intent.ACTION_THEME_RESOURCES_CACHED);
+ intent.putExtra(Intent.EXTRA_THEME_PACKAGE_NAME, themePkgName);
+ intent.putExtra(Intent.EXTRA_THEME_RESULT, resultCode);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ /**
+ * Posts a notification to let the user know the theme was not installed.
+ * @param name
+ */
+ private void postFailedThemeInstallNotification(String name) {
+ NotificationManager nm =
+ (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ Notification notice = new Notification.Builder(mContext)
+ .setAutoCancel(true)
+ .setOngoing(false)
+ .setContentTitle(
+ mContext.getString(R.string.theme_install_error_title))
+ .setContentText(String.format(mContext.getString(
+ R.string.theme_install_error_message),
+ name))
+ .setSmallIcon(android.R.drawable.stat_notify_error)
+ .setWhen(System.currentTimeMillis())
+ .build();
+ nm.notify(name.hashCode(), notice);
+ }
+
+ private String getThemeName(PackageInfo pi) {
+ if (pi.themeInfo != null) {
+ return pi.themeInfo.name;
+ } else if (pi.isLegacyIconPackApk) {
+ return pi.applicationInfo.name;
+ }
+
+ return null;
+ }
+}
diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java
index e0e6070..1b82ed6 100644
--- a/services/core/java/com/android/server/WiredAccessoryManager.java
+++ b/services/core/java/com/android/server/WiredAccessoryManager.java
@@ -66,6 +66,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
private static final String NAME_H2W = "h2w";
private static final String NAME_USB_AUDIO = "usb_audio";
+ private static final String NAME_SAMSUNG_USB_AUDIO = "dock";
private static final String NAME_HDMI_AUDIO = "hdmi_audio";
private static final String NAME_HDMI = "hdmi";
@@ -330,7 +331,8 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
FileReader file = new FileReader(uei.getSwitchStatePath());
int len = file.read(buffer, 0, 1024);
file.close();
- curState = Integer.valueOf((new String(buffer, 0, len)).trim());
+ curState = validateSwitchState(
+ Integer.valueOf((new String(buffer, 0, len)).trim()));
if (curState > 0) {
updateStateLocked(uei.getDevPath(), uei.getDevName(), curState);
@@ -345,14 +347,23 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
}
// At any given time accessories could be inserted
- // one on the board, one on the dock and one on HDMI:
- // observe three UEVENTs
+ // one on the board, one on the dock, one on the
+ // samsung dock and one on HDMI:
+ // observe all UEVENTs that have valid switch supported
+ // by the Kernel
for (int i = 0; i < mUEventInfo.size(); ++i) {
UEventInfo uei = mUEventInfo.get(i);
startObserving("DEVPATH="+uei.getDevPath());
}
}
+ private int validateSwitchState(int state) {
+ // Some drivers, namely HTC headset ones, add additional bits to
+ // the switch state. As we only are able to deal with the states
+ // 0, 1 and 2, mask out all the other bits
+ return state & 0x3;
+ }
+
private List<UEventInfo> makeObservedUEventList() {
List<UEventInfo> retVal = new ArrayList<UEventInfo>();
UEventInfo uei;
@@ -375,6 +386,15 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
Slog.w(TAG, "This kernel does not have usb audio support");
}
+ // Monitor Samsung USB audio
+ uei = new UEventInfo(NAME_SAMSUNG_USB_AUDIO, BIT_USB_HEADSET_DGTL,
+ BIT_USB_HEADSET_ANLG, 0);
+ if (uei.checkSwitchExists()) {
+ retVal.add(uei);
+ } else {
+ Slog.w(TAG, "This kernel does not have samsung usb dock audio support");
+ }
+
// Monitor HDMI
//
// If the kernel has support for the "hdmi_audio" switch, use that. It will be
@@ -405,7 +425,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
try {
String devPath = event.get("DEVPATH");
String name = event.get("SWITCH_NAME");
- int state = Integer.parseInt(event.get("SWITCH_STATE"));
+ int state = validateSwitchState(Integer.parseInt(event.get("SWITCH_STATE")));
synchronized (mLock) {
updateStateLocked(devPath, name, state);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2f3cdf7..fb65a15 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1,6 +1,8 @@
/*
* Copyright (C) 2006-2008 The Android Open Source Project
- *
+ * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc.
+ * Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
* 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
@@ -52,6 +54,7 @@ import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetManager;
import android.content.pm.PermissionInfo;
import android.content.res.Resources;
+import android.content.res.ThemeConfig;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
@@ -71,8 +74,8 @@ import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.SparseIntArray;
import android.view.Display;
-import android.util.BoostFramework;
+import android.view.InflateException;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.AssistUtils;
@@ -171,6 +174,7 @@ import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
+import android.content.pm.ThemeUtils;
import android.content.pm.UserInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PathPermission;
@@ -179,6 +183,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
+import android.content.res.ThemeConfig;
import android.net.Proxy;
import android.net.ProxyInfo;
import android.net.Uri;
@@ -389,6 +394,8 @@ public final class ActivityManagerService extends ActivityManagerNative
// How many bytes to write into the dropbox log before truncating
static final int DROPBOX_MAX_SIZE = 256 * 1024;
+ static final String PROP_REFRESH_THEME = "sys.refresh_theme";
+
// Access modes for handleIncomingUser.
static final int ALLOW_NON_FULL = 0;
static final int ALLOW_NON_FULL_IN_PROFILE = 1;
@@ -1012,6 +1019,7 @@ public final class ActivityManagerService extends ActivityManagerNative
boolean mLaunchWarningShown = false;
Context mContext;
+ Context mUiContext;
int mFactoryTest;
@@ -1363,6 +1371,8 @@ public final class ActivityManagerService extends ActivityManagerNative
static final int REPORT_TIME_TRACKER_MSG = 55;
static final int REPORT_USER_SWITCH_COMPLETE_MSG = 56;
static final int SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG = 57;
+ static final int POST_PRIVACY_NOTIFICATION_MSG = 58;
+ static final int CANCEL_PRIVACY_NOTIFICATION_MSG = 59;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1435,7 +1445,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return;
}
if (mShowDialogs && !mSleeping && !mShuttingDown) {
- Dialog d = new AppErrorDialog(mContext,
+ Dialog d = new AppErrorDialog(getUiContext(),
ActivityManagerService.this, res, proc);
d.show();
proc.crashDialog = d;
@@ -1470,7 +1480,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (mShowDialogs) {
Dialog d = new AppNotRespondingDialog(ActivityManagerService.this,
- mContext, proc, (ActivityRecord)data.get("activity"),
+ getUiContext(), proc, (ActivityRecord)data.get("activity"),
msg.arg1 != 0);
d.show();
proc.anrDialog = d;
@@ -1496,7 +1506,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
AppErrorResult res = (AppErrorResult) data.get("result");
if (mShowDialogs && !mSleeping && !mShuttingDown) {
- Dialog d = new StrictModeViolationDialog(mContext,
+ Dialog d = new StrictModeViolationDialog(getUiContext(),
ActivityManagerService.this, res, proc);
d.show();
proc.crashDialog = d;
@@ -1510,7 +1520,7 @@ public final class ActivityManagerService extends ActivityManagerNative
} break;
case SHOW_FACTORY_ERROR_MSG: {
Dialog d = new FactoryErrorDialog(
- mContext, msg.getData().getCharSequence("msg"));
+ getUiContext(), msg.getData().getCharSequence("msg"));
d.show();
ensureBootCompleted();
} break;
@@ -1521,7 +1531,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (!app.waitedForDebugger) {
Dialog d = new AppWaitingForDebuggerDialog(
ActivityManagerService.this,
- mContext, app);
+ getUiContext(), app);
app.waitDialog = d;
app.waitedForDebugger = true;
d.show();
@@ -2048,6 +2058,69 @@ public final class ActivityManagerService extends ActivityManagerNative
// it is finished we make sure it is reset to its default.
mUserIsMonkey = false;
} break;
+ case POST_PRIVACY_NOTIFICATION_MSG: {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+
+ ActivityRecord root = (ActivityRecord)msg.obj;
+ ProcessRecord process = root.app;
+ if (process == null) {
+ return;
+ }
+
+ try {
+ Context context = mContext.createPackageContext(process.info.packageName, 0);
+ String text = mContext.getString(R.string.privacy_guard_notification_detail,
+ context.getApplicationInfo().loadLabel(context.getPackageManager()));
+ String title = mContext.getString(R.string.privacy_guard_notification);
+
+ Intent infoIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
+ Uri.fromParts("package", root.packageName, null));
+
+ Notification notification = new Notification();
+ notification.icon = com.android.internal.R.drawable.stat_notify_privacy_guard;
+ notification.when = 0;
+ notification.flags = Notification.FLAG_ONGOING_EVENT;
+ notification.priority = Notification.PRIORITY_LOW;
+ notification.defaults = 0;
+ notification.sound = null;
+ notification.vibrate = null;
+ notification.setLatestEventInfo(mContext,
+ title, text,
+ PendingIntent.getActivityAsUser(mContext, 0, infoIntent,
+ PendingIntent.FLAG_CANCEL_CURRENT, null,
+ new UserHandle(root.userId)));
+
+ try {
+ int[] outId = new int[1];
+ inm.enqueueNotificationWithTag("android", "android", null,
+ R.string.privacy_guard_notification,
+ notification, outId, root.userId);
+ } catch (RuntimeException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Error showing notification for privacy guard", e);
+ } catch (RemoteException e) {
+ }
+ } catch (NameNotFoundException e) {
+ Slog.w(TAG, "Unable to create context for privacy guard notification", e);
+ }
+ } break;
+ case CANCEL_PRIVACY_NOTIFICATION_MSG: {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+ try {
+ inm.cancelNotificationWithTag("android", null,
+ R.string.privacy_guard_notification, msg.arg1);
+ } catch (RuntimeException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Error canceling notification for service", e);
+ } catch (RemoteException e) {
+ }
+ } break;
}
}
};
@@ -2596,6 +2669,15 @@ public final class ActivityManagerService extends ActivityManagerNative
-1, Process.SYSTEM_UID, UserHandle.USER_ALL);
}
+ private Context getUiContext() {
+ synchronized (this) {
+ if (mUiContext == null && mBooted) {
+ mUiContext = ThemeUtils.createUiContext(mContext);
+ }
+ return mUiContext != null ? mUiContext : mContext;
+ }
+ }
+
/**
* Initialize the application bind args. These are passed to each
* process when the bindApplication() IPC is sent to the process. They're
@@ -3320,6 +3402,13 @@ public final class ActivityManagerService extends ActivityManagerNative
debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
}
+ //Check if zygote should refresh its fonts
+ boolean refreshTheme = false;
+ if (SystemProperties.getBoolean(PROP_REFRESH_THEME, false)) {
+ SystemProperties.set(PROP_REFRESH_THEME, "false");
+ refreshTheme = true;
+ }
+
String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
if (requiredAbi == null) {
requiredAbi = Build.SUPPORTED_ABIS[0];
@@ -3344,7 +3433,7 @@ public final class ActivityManagerService extends ActivityManagerNative
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
- app.info.dataDir, entryPointArgs);
+ app.info.dataDir, refreshTheme, entryPointArgs);
checkTime(startTime, "startProcess: returned from zygote!");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -3366,17 +3455,6 @@ public final class ActivityManagerService extends ActivityManagerNative
checkTime(startTime, "startProcess: building log message");
StringBuilder buf = mStringBuilder;
buf.setLength(0);
- if(hostingType.equals("activity"))
- {
- BoostFramework mPerf = null;
- if (null == mPerf) {
- mPerf = new BoostFramework();
- }
- if (mPerf != null) {
- mPerf.perfIOPrefetchStart(startResult.pid,app.processName);
- }
- }
-
buf.append("Start proc ");
buf.append(startResult.pid);
buf.append(':');
@@ -3866,7 +3944,8 @@ public final class ActivityManagerService extends ActivityManagerNative
if (sourceRecord == null) {
throw new SecurityException("Called with bad activity token: " + resultTo);
}
- if (!sourceRecord.info.packageName.equals("android")) {
+ if (!sourceRecord.info.packageName.equals("android") &&
+ !sourceRecord.info.packageName.equals("org.cyanogenmod.resolver")) {
throw new SecurityException(
"Must be called from an activity that is declared in the android package");
}
@@ -5115,7 +5194,7 @@ public final class ActivityManagerService extends ActivityManagerNative
@Override
public void run() {
synchronized (ActivityManagerService.this) {
- final Dialog d = new LaunchWarningWindow(mContext, cur, next);
+ final Dialog d = new LaunchWarningWindow(getUiContext(), cur, next);
d.show();
mUiHandler.postDelayed(new Runnable() {
@Override
@@ -6315,14 +6394,16 @@ public final class ActivityManagerService extends ActivityManagerNative
@Override
public void keyguardGoingAway(boolean disableWindowAnimations,
- boolean keyguardGoingToNotificationShade) {
+ boolean keyguardGoingToNotificationShade,
+ boolean keyguardShowingMedia) {
enforceNotIsolatedCaller("keyguardGoingAway");
final long token = Binder.clearCallingIdentity();
try {
synchronized (this) {
if (DEBUG_LOCKSCREEN) logLockScreen("");
mWindowManager.keyguardGoingAway(disableWindowAnimations,
- keyguardGoingToNotificationShade);
+ keyguardGoingToNotificationShade,
+ keyguardShowingMedia);
if (mLockScreenShown == LOCK_SCREEN_SHOWN) {
mLockScreenShown = LOCK_SCREEN_HIDDEN;
updateSleepIfNeededLocked();
@@ -6388,6 +6469,13 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}, dumpheapFilter);
+ ThemeUtils.registerThemeChangeReceiver(mContext, new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mUiContext = null;
+ }
+ });
+
// Let system services know.
mSystemServiceManager.startBootPhase(SystemService.PHASE_BOOT_COMPLETED);
@@ -6439,7 +6527,7 @@ public final class ActivityManagerService extends ActivityManagerNative
},
0, null, null,
new String[] {android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
- AppOpsManager.OP_NONE, null, true, false,
+ AppOpsManager.OP_BOOT_COMPLETED, null, true, false,
MY_PID, Process.SYSTEM_UID, userId);
}
}
@@ -10244,7 +10332,13 @@ public final class ActivityManagerService extends ActivityManagerNative
if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
app.persistent = true;
- app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
+
+ // The Adj score defines an order of processes to be killed.
+ // If a process is shared by multiple apps, maxAdj must be set by the highest
+ // prioritized app to avoid being killed.
+ if (app.maxAdj >= ProcessList.PERSISTENT_PROC_ADJ) {
+ app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
+ }
}
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
@@ -10682,6 +10776,7 @@ public final class ActivityManagerService extends ActivityManagerNative
public void requestBugReport() {
enforceCallingPermission(android.Manifest.permission.DUMP, "requestBugReport");
+ mContext.sendBroadcast(new Intent("android.intent.action.BUGREPORT_STARTED"));
SystemProperties.set("ctl.start", "bugreport");
}
@@ -10759,9 +10854,15 @@ public final class ActivityManagerService extends ActivityManagerNative
return true;
}
}
+ final int anrPid = proc.pid;
mHandler.post(new Runnable() {
@Override
public void run() {
+ if (anrPid != proc.pid) {
+ Slog.i(TAG, "Ignoring stale ANR (occurred in " + anrPid +
+ ", but current pid is " + proc.pid + ")");
+ return;
+ }
appNotResponding(proc, activity, parent, aboveSystem, annotation);
}
});
@@ -11984,6 +12085,28 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ private void sendAppFailureBroadcast(String pkgName) {
+ Intent intent = new Intent(Intent.ACTION_APP_FAILURE,
+ (pkgName != null)? Uri.fromParts("package", pkgName, null) : null);
+ mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT_OR_SELF);
+ }
+
+ /**
+ * A possible theme crash is one that throws one of the following exceptions
+ * {@link android.content.res.Resources.NotFoundException}
+ * {@link android.view.InflateException}
+ *
+ * @param exceptionClassName
+ * @return True if exceptionClassName is one of the above exceptions
+ */
+ private boolean isPossibleThemeCrash(String exceptionClassName) {
+ if (Resources.NotFoundException.class.getName().equals(exceptionClassName) ||
+ InflateException.class.getName().equals(exceptionClassName)) {
+ return true;
+ }
+ return false;
+ }
+
private boolean handleAppCrashLocked(ProcessRecord app, String reason,
String shortMsg, String longMsg, String stackTrace) {
long now = SystemClock.uptimeMillis();
@@ -11994,6 +12117,9 @@ public final class ActivityManagerService extends ActivityManagerNative
} else {
crashTime = null;
}
+
+ if (isPossibleThemeCrash(shortMsg)) sendAppFailureBroadcast(app.info.packageName);
+
if (crashTime != null && now < crashTime+ProcessList.MIN_CRASH_INTERVAL) {
// This process loses!
Slog.w(TAG, "Process " + app.info.processName
@@ -12793,6 +12919,9 @@ public final class ActivityManagerService extends ActivityManagerNative
|| (!allUids && app.uid != callingUid)) {
continue;
}
+ if (app.processName.equals("system")) {
+ continue;
+ }
if ((app.thread != null) && (!app.crashing && !app.notResponding)) {
// Generate process state info for running application
ActivityManager.RunningAppProcessInfo currApp =
@@ -17290,6 +17419,9 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized(this) {
ci = new Configuration(mConfiguration);
ci.userSetLocale = false;
+ if (ci.themeConfig == null) {
+ ci.themeConfig = ThemeConfig.getBootTheme(mContext.getContentResolver());
+ }
}
return ci;
}
@@ -17379,6 +17511,11 @@ public final class ActivityManagerService extends ActivityManagerNative
values.locale));
}
+ if (values.themeConfig != null) {
+ saveThemeResourceLocked(values.themeConfig,
+ !values.themeConfig.equals(mConfiguration.themeConfig));
+ }
+
mConfigurationSeq++;
if (mConfigurationSeq <= 0) {
mConfigurationSeq = 1;
@@ -17434,6 +17571,10 @@ public final class ActivityManagerService extends ActivityManagerNative
null, AppOpsManager.OP_NONE, null, false, false,
MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) {
+ // if locale changed, time format may have changed
+ final int is24Hour = android.text.format.DateFormat.is24HourFormat(mContext) ? 1 : 0;
+ mHandler.sendMessage(mHandler.obtainMessage(UPDATE_TIME, is24Hour, 0));
+ // now send general broadcast
intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
if (!mProcessesReady) {
@@ -17531,6 +17672,14 @@ public final class ActivityManagerService extends ActivityManagerNative
return srec.launchedFromPackage;
}
+ private void saveThemeResourceLocked(ThemeConfig t, boolean isDiff){
+ if(isDiff) {
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Configuration.THEME_PKG_CONFIGURATION_PERSISTENCE_PROPERTY, t.toJson(),
+ UserHandle.USER_CURRENT);
+ }
+ }
+
// =========================================================
// LIFETIME MANAGEMENT
// =========================================================
@@ -20268,7 +20417,7 @@ public final class ActivityManagerService extends ActivityManagerNative
broadcastIntentLocked(null, null, intent,
null, null, 0, null, null,
new String[] {android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
- AppOpsManager.OP_NONE, null, true, false, MY_PID, Process.SYSTEM_UID,
+ AppOpsManager.OP_BOOT_COMPLETED, null, true, false, MY_PID, Process.SYSTEM_UID,
userId);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index a254451..1f1ed6f 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -40,6 +40,7 @@ import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.AppGlobals;
+import android.app.AppOpsManager;
import android.app.IActivityController;
import android.app.ResultInfo;
import android.app.ActivityManager.RunningTaskInfo;
@@ -66,8 +67,10 @@ import android.service.voice.IVoiceInteractionSession;
import android.util.EventLog;
import android.util.Slog;
import android.view.Display;
-import android.util.BoostFramework;
-import com.android.internal.app.ActivityTrigger;
+
+import com.android.server.LocalServices;
+
+import cyanogenmod.power.PerformanceManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -153,10 +156,6 @@ final class ActivityStack {
final WindowManagerService mWindowManager;
private final RecentTasks mRecentTasks;
- public BoostFramework mPerf = null;
- public boolean mIsAnimationBoostEnabled = false;
- public int aBoostTimeOut = 0;
- public int aBoostParamVal[];
/**
* The back history of all previous (and possibly still
* running) activities. It contains #TaskRecord objects.
@@ -270,9 +269,9 @@ final class ActivityStack {
}
}
- final Handler mHandler;
+ private final PerformanceManagerInternal mPerf;
- static final ActivityTrigger mActivityTrigger = new ActivityTrigger();
+ final Handler mHandler;
final class ActivityStackHandler extends Handler {
@@ -370,14 +369,7 @@ final class ActivityStack {
mCurrentUser = mService.mCurrentUserId;
mRecentTasks = recentTasks;
mOverrideConfig = Configuration.EMPTY;
- mIsAnimationBoostEnabled = mService.mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_enablePerfBoostForAnimation);
- if (mIsAnimationBoostEnabled) {
- aBoostTimeOut = mService.mContext.getResources().getInteger(
- com.android.internal.R.integer.animationboost_timeout_param);
- aBoostParamVal = mService.mContext.getResources().getIntArray(
- com.android.internal.R.array.animationboost_param_value);
- }
+ mPerf = LocalServices.getService(PerformanceManagerInternal.class);
}
boolean okToShowLocked(ActivityRecord r) {
@@ -1146,6 +1138,8 @@ final class ActivityStack {
// When resuming an activity, require it to call requestVisibleBehind() again.
mActivityContainer.mActivityDisplay.setVisibleBehindActivity(null);
}
+
+ updatePrivacyGuardNotificationLocked(next);
}
private void setVisible(ActivityRecord r, boolean visible) {
@@ -1698,8 +1692,11 @@ final class ActivityStack {
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
- mActivityTrigger.activityResumeTrigger(next.intent, next.info, next.appInfo);
-
+ // Some activities may want to alter the system power management
+ if (mStackSupervisor.mPerf != null) {
+ mStackSupervisor.mPerf.activityResumed(next.intent);
+ }
+
// If we are currently pausing an activity, then don't do anything
// until that is done.
if (!mStackSupervisor.allPausedActivitiesComplete()) {
@@ -1821,9 +1818,6 @@ final class ActivityStack {
// that the previous one will be hidden soon. This way it can know
// to ignore it when computing the desired screen orientation.
boolean anim = true;
- if (mIsAnimationBoostEnabled == true && mPerf == null) {
- mPerf = new BoostFramework();
- }
if (prev != null) {
if (prev.finishing) {
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
@@ -1835,8 +1829,10 @@ final class ActivityStack {
mWindowManager.prepareAppTransition(prev.task == next.task
? AppTransition.TRANSIT_ACTIVITY_CLOSE
: AppTransition.TRANSIT_TASK_CLOSE, false);
- if(prev.task != next.task && mPerf != null) {
- mPerf.perfLockAcquire(aBoostTimeOut, aBoostParamVal);
+ if (prev.task != next.task) {
+ if (mStackSupervisor.mPerf != null) {
+ mStackSupervisor.mPerf.cpuBoost(2000 * 1000);
+ }
}
}
mWindowManager.setAppWillBeHidden(prev.appToken);
@@ -1853,8 +1849,10 @@ final class ActivityStack {
: next.mLaunchTaskBehind
? AppTransition.TRANSIT_TASK_OPEN_BEHIND
: AppTransition.TRANSIT_TASK_OPEN, false);
- if(prev.task != next.task && mPerf != null) {
- mPerf.perfLockAcquire(aBoostTimeOut, aBoostParamVal);
+ if (prev.task != next.task) {
+ if (mStackSupervisor.mPerf != null) {
+ mStackSupervisor.mPerf.cpuBoost(2000 * 1000);
+ }
}
}
}
@@ -2098,6 +2096,29 @@ final class ActivityStack {
updateTaskMovement(task, true);
}
+ private final void updatePrivacyGuardNotificationLocked(ActivityRecord next) {
+
+ String privacyGuardPackageName = mStackSupervisor.mPrivacyGuardPackageName;
+ if (privacyGuardPackageName != null && privacyGuardPackageName.equals(next.packageName)) {
+ return;
+ }
+
+ boolean privacy = mService.mAppOpsService.getPrivacyGuardSettingForPackage(
+ next.app.uid, next.packageName);
+
+ if (privacyGuardPackageName != null && !privacy) {
+ Message msg = mService.mHandler.obtainMessage(
+ ActivityManagerService.CANCEL_PRIVACY_NOTIFICATION_MSG, next.userId);
+ msg.sendToTarget();
+ mStackSupervisor.mPrivacyGuardPackageName = null;
+ } else if (privacy) {
+ Message msg = mService.mHandler.obtainMessage(
+ ActivityManagerService.POST_PRIVACY_NOTIFICATION_MSG, next);
+ msg.sendToTarget();
+ mStackSupervisor.mPrivacyGuardPackageName = next.packageName;
+ }
+ }
+
final void startActivityLocked(ActivityRecord r, boolean newTask,
boolean doResume, boolean keepCurTransition, Bundle options) {
TaskRecord rTask = r.task;
@@ -2168,7 +2189,6 @@ final class ActivityStack {
task.setFrontOfTask();
r.putInHistory();
- mActivityTrigger.activityStartTrigger(r.intent, r.info, r.appInfo);
if (!isHomeStack() || numActivities() > 0) {
// We want to show the starting preview window if we are
// switching to a new task, or the next activity's process is
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index a4c8882..81cb623 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -101,13 +101,12 @@ import android.util.ArraySet;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.BoostFramework;
-
import android.util.SparseIntArray;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.InputEvent;
import android.view.Surface;
+
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.ReferrerIntent;
@@ -119,6 +118,8 @@ import com.android.server.LocalServices;
import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.wm.WindowManagerService;
+import cyanogenmod.power.PerformanceManagerInternal;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
@@ -161,15 +162,6 @@ public final class ActivityStackSupervisor implements DisplayListener {
static final int RESUME_TOP_ACTIVITY_MSG = FIRST_SUPERVISOR_STACK_MSG + 2;
static final int SLEEP_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 3;
static final int LAUNCH_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 4;
- public BoostFramework mPerf_iop = null;
- public BoostFramework mPerfBoost = null;
- public BoostFramework mPerfPack = null;
- public boolean mIsPerfBoostEnabled = false;
- public boolean mIsperfDisablepackingEnable = false;
- public int lBoostTimeOut = 0;
- public int lDisPackTimeOut = 0;
- public int lBoostCpuParamVal[];
- public int lBoostPackParamVal[];
static final int HANDLE_DISPLAY_ADDED = FIRST_SUPERVISOR_STACK_MSG + 5;
static final int HANDLE_DISPLAY_CHANGED = FIRST_SUPERVISOR_STACK_MSG + 6;
static final int HANDLE_DISPLAY_REMOVED = FIRST_SUPERVISOR_STACK_MSG + 7;
@@ -282,6 +274,15 @@ public final class ActivityStackSupervisor implements DisplayListener {
* setWindowManager is called. **/
private boolean mLeanbackOnlyDevice;
+ PowerManager mPm;
+
+ PerformanceManagerInternal mPerf;
+
+ /**
+ * Is the privacy guard currently enabled? Shared between ActivityStacks
+ */
+ String mPrivacyGuardPackageName = null;
+
/**
* We don't want to allow the device to go to sleep while in the process
* of launching an activity. This is primarily to allow alarm intent
@@ -351,35 +352,27 @@ public final class ActivityStackSupervisor implements DisplayListener {
mService = service;
mRecentTasks = recentTasks;
mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper());
- /* Is perf lock for cpu-boost enabled during App 1st launch */
- mIsPerfBoostEnabled = mService.mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_enableCpuBoostForAppLaunch);
- mIsperfDisablepackingEnable = mService.mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_disablePacking);
+ }
- if(mIsPerfBoostEnabled) {
- lBoostTimeOut = mService.mContext.getResources().getInteger(
- com.android.internal.R.integer.launchboost_timeout_param);
- lBoostCpuParamVal = mService.mContext.getResources().getIntArray(
- com.android.internal.R.array.launchboost_param_value);
+ private void launchBoost() {
+ if (mPerf == null) {
+ mPerf = LocalServices.getService(PerformanceManagerInternal.class);
+ }
+ if (mPerf == null) {
+ Slog.e(TAG, "PerformanceManager not ready!");
+ } else {
+ mPerf.launchBoost();
}
-
- if(mIsperfDisablepackingEnable) {
- lDisPackTimeOut = mService.mContext.getResources().getInteger(
- com.android.internal.R.integer.disablepacking_timeout_param);
- lBoostPackParamVal = mService.mContext.getResources().getIntArray(
- com.android.internal.R.array.launchboost_packing_param_value);
- }
}
-
+
/**
* At the time when the constructor runs, the power manager has not yet been
* initialized. So we initialize our wakelocks afterwards.
*/
void initPowerManagement() {
- PowerManager pm = (PowerManager)mService.mContext.getSystemService(Context.POWER_SERVICE);
- mGoingToSleep = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
- mLaunchingActivity = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*launch*");
+ mPm = (PowerManager)mService.mContext.getSystemService(Context.POWER_SERVICE);
+ mGoingToSleep = mPm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
+ mLaunchingActivity = mPm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*launch*");
mLaunchingActivity.setReferenceCounted(false);
}
@@ -1457,6 +1450,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
Display.DEFAULT_DISPLAY : mFocusedStack.mDisplayId) :
(container.mActivityDisplay == null ? Display.DEFAULT_DISPLAY :
container.mActivityDisplay.mDisplayId)));
+ /* Acquire perf lock during new app launch */
+ launchBoost();
}
ActivityRecord sourceRecord = null;
@@ -1509,6 +1504,11 @@ public final class ActivityStackSupervisor implements DisplayListener {
if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
// We couldn't find a class that can handle the given Intent.
// That's the end of that!
+ final Uri data = intent.getData();
+ final String strData = data != null ? data.toSafeString() : null;
+ EventLog.writeEvent(EventLogTags.AM_INTENT_NOT_RESOLVED, callingPackage,
+ intent.getAction(), intent.getType(), strData, intent.getFlags());
+
err = ActivityManager.START_INTENT_NOT_RESOLVED;
}
@@ -2797,12 +2797,10 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
void findTaskToMoveToFrontLocked(TaskRecord task, int flags, Bundle options, String reason) {
-
- ActivityRecord top_activity;
- top_activity = task.stack.topRunningActivityLocked(null);
+ ActivityRecord top = task.stack.topRunningActivityLocked(null);
/* App is launching from recent apps and it's a new process */
- if(top_activity != null && top_activity.state == ActivityState.DESTROYED) {
- acquireAppLaunchPerfLock();
+ if(top != null && top.state == ActivityState.DESTROYED) {
+ launchBoost();
}
if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
@@ -3069,23 +3067,6 @@ public final class ActivityStackSupervisor implements DisplayListener {
resumeTopActivitiesLocked();
}
- void acquireAppLaunchPerfLock() {
- /* Acquire perf lock during new app launch */
- if (mIsperfDisablepackingEnable == true && mPerfPack == null) {
- mPerfPack = new BoostFramework();
- }
- if (mPerfPack != null) {
- mPerfPack.perfLockAcquire(lDisPackTimeOut, lBoostPackParamVal);
- }
-
- if (mIsPerfBoostEnabled == true && mPerfBoost == null) {
- mPerfBoost = new BoostFramework();
- }
- if (mPerfBoost != null) {
- mPerfBoost.perfLockAcquire(lBoostTimeOut, lBoostCpuParamVal);
- }
- }
-
ActivityRecord findTaskLocked(ActivityRecord r) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
@@ -3103,33 +3084,16 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
final ActivityRecord ar = stack.findTaskLocked(r);
if (ar != null) {
- if(ar.state == ActivityState.DESTROYED ) {
- /*It's a new app launch */
- acquireAppLaunchPerfLock();
-
- // Strat IOP
- if (mPerf_iop == null) {
- mPerf_iop = new BoostFramework();
- }
- if (mPerf_iop != null) {
- mPerf_iop.perfIOPrefetchStart(-1,r.packageName);
- }
+ if (ar.state == ActivityState.DESTROYED) {
+ launchBoost();
}
return ar;
}
}
}
- /* Acquire perf lock during new app launch */
- acquireAppLaunchPerfLock();
- //Start IOP
- if (mPerf_iop == null) {
- mPerf_iop = new BoostFramework();
- }
- if (mPerf_iop != null) {
- mPerf_iop.perfIOPrefetchStart(-1,r.packageName);
- }
-
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "No task found");
+ launchBoost();
+
return null;
}
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 9a645df..33fa714 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -78,6 +78,8 @@ option java_package com.android.server.am
30036 am_provider_lost_process (User|1|5),(Package Name|3),(UID|1|5),(Name|3)
# The activity manager gave up on a new process taking too long to start
30037 am_process_start_timeout (User|1|5),(PID|1|5),(UID|1|5),(Process Name|3)
+# The activity manager was unable to resolve the intent to an Activity
+30038 am_intent_not_resolved (Calling Package Name|3),(Action|3),(MIME Type|3),(URI|3),(Flags|1|5)
# Unhandled exception
30039 am_crash (User|1|5),(PID|1|5),(Process Name|3),(Flags|1|5),(Exception|3),(Message|3),(File|3),(Line|1|5)
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index cde2d7c..1d72fd9 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -59,6 +59,7 @@ import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioManagerInternal;
import android.media.AudioPort;
+import android.media.AudioRecord;
import android.media.AudioRoutesInfo;
import android.media.IAudioFocusDispatcher;
import android.media.IAudioRoutesObserver;
@@ -405,6 +406,15 @@ public class AudioService extends IAudioService.Stub {
* @see System#MUTE_STREAMS_AFFECTED */
private int mMuteAffectedStreams;
+ /** @see #handleHotwordInput **/
+ private Object mHotwordInputLock = new Object();
+
+ /** The package name of the application that is
+ * currently using the HOTWORD input.
+ */
+ // protected by mHotwordInputLock
+ private String mHotwordAudioInputPackage;
+
/**
* NOTE: setVibrateSetting(), getVibrateSetting(), shouldVibrate() are deprecated.
* mVibrateSetting is just maintained during deprecation period but vibration policy is
@@ -542,7 +552,6 @@ public class AudioService extends IAudioService.Stub {
// Devices for which the volume is fixed and VolumePanel slider should be disabled
int mFixedVolumeDevices = AudioSystem.DEVICE_OUT_HDMI |
AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET |
- AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET |
AudioSystem.DEVICE_OUT_HDMI_ARC |
AudioSystem.DEVICE_OUT_SPDIF |
AudioSystem.DEVICE_OUT_AUX_LINE;
@@ -554,6 +563,9 @@ public class AudioService extends IAudioService.Stub {
private boolean mDockAudioMediaEnabled = true;
+ private boolean mForceAnalogDeskDock;
+ private boolean mForceAnalogCarDock;
+
private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
// Used when safe volume warning message display is requested by setStreamVolume(). In this
@@ -657,6 +669,11 @@ public class AudioService extends IAudioService.Stub {
mUseFixedVolume = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useFixedVolume);
+ mForceAnalogDeskDock = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_forceAnalogDeskDock);
+ mForceAnalogCarDock = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_forceAnalogCarDock);
+
// must be called before readPersistedSettings() which needs a valid mStreamVolumeAlias[]
// array initialized by updateStreamVolumeAlias()
updateStreamVolumeAlias(false /*updateVolumes*/, TAG);
@@ -1601,6 +1618,46 @@ public class AudioService extends IAudioService.Stub {
sendVolumeUpdate(streamType, oldIndex, index, flags);
}
+ /**
+ * Retrieve the package name of the application that currently controls
+ * the {@link android.media.MediaRecorder.AudioSource#HOTWORD} input.
+ * @return The package name of the application that controls the input
+ * or null if no package currently controls it.
+ */
+ public String getCurrentHotwordInputPackageName() {
+ return mHotwordAudioInputPackage;
+ }
+
+ /**
+ * Handle the change of state of the HOTWORD input.
+ *
+ * When the {@link android.media.MediaRecorder.AudioSource#HOTWORD} input is
+ * in use, send a broadcast to alert the new state and store the name of the current
+ * package that controls the input in mHotwordAudioInputPackage.
+ * @param listening Whether the input is being listened to.
+ */
+ public void handleHotwordInput(boolean listening) {
+ synchronized (mHotwordInputLock) {
+ Intent broadcastIntent = new Intent(Intent.ACTION_HOTWORD_INPUT_CHANGED);
+ String[] packages = mContext.getPackageManager().getPackagesForUid(
+ Binder.getCallingUid());
+ if (packages.length > 0) {
+ if (listening) {
+ mHotwordAudioInputPackage = packages[0];
+ }
+ broadcastIntent.putExtra(Intent.EXTRA_CURRENT_PACKAGE_NAME, packages[0]);
+ }
+ broadcastIntent.putExtra(Intent.EXTRA_HOTWORD_INPUT_STATE,
+ listening ? AudioRecord.RECORDSTATE_RECORDING :
+ AudioRecord.RECORDSTATE_STOPPED);
+ // Set the currently listening package to null if listening has stopped.
+ if (!listening) {
+ mHotwordAudioInputPackage = null;
+ }
+ sendBroadcastToAll(broadcastIntent, Manifest.permission.CAPTURE_AUDIO_HOTWORD);
+ }
+ }
+
/** @see AudioManager#forceVolumeControlStream(int) */
public void forceVolumeControlStream(int streamType, IBinder cb) {
synchronized(mForceControlStreamLock) {
@@ -1653,11 +1710,15 @@ public class AudioService extends IAudioService.Stub {
}
private void sendBroadcastToAll(Intent intent) {
+ sendBroadcastToAll(intent, null);
+ }
+
+ private void sendBroadcastToAll(Intent intent, String receiverPermission) {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
final long ident = Binder.clearCallingIdentity();
try {
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL, receiverPermission);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -5158,10 +5219,12 @@ public class AudioService extends IAudioService.Stub {
int config;
switch (dockState) {
case Intent.EXTRA_DOCK_STATE_DESK:
- config = AudioSystem.FORCE_BT_DESK_DOCK;
+ config = mForceAnalogDeskDock ? AudioSystem.FORCE_ANALOG_DOCK :
+ AudioSystem.FORCE_BT_DESK_DOCK;
break;
case Intent.EXTRA_DOCK_STATE_CAR:
- config = AudioSystem.FORCE_BT_CAR_DOCK;
+ config = mForceAnalogCarDock ? AudioSystem.FORCE_ANALOG_DOCK :
+ AudioSystem.FORCE_BT_CAR_DOCK;
break;
case Intent.EXTRA_DOCK_STATE_LE_DESK:
config = AudioSystem.FORCE_ANALOG_DOCK;
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 1ead64d..89569ec 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -131,6 +131,7 @@ public class Nat464Xlat extends BaseNetworkObserver {
}
try {
+ mNMService.unregisterObserver(this);
mNMService.registerObserver(this);
} catch(RemoteException e) {
Slog.e(TAG, "startClat: Can't register interface observer for clat on " + mNetwork);
@@ -239,16 +240,12 @@ public class Nat464Xlat extends BaseNetworkObserver {
}
@Override
- public void interfaceLinkStateChanged(String iface, boolean up) {
+ public void addressUpdated(String iface, LinkAddress clatAddress) {
// Called by the InterfaceObserver on its own thread, so can race with stop().
- if (isStarted() && up && mIface.equals(iface)) {
+ if (isStarted() && mIface.equals(iface) && clatAddress != null) {
Slog.i(TAG, "interface " + iface + " is up, mIsRunning " + mIsRunning + "->true");
if (!mIsRunning) {
- LinkAddress clatAddress = getLinkAddress(iface);
- if (clatAddress == null) {
- return;
- }
mIsRunning = true;
maybeSetIpv6NdOffload(mBaseIface, false);
LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index f106b75..9ead529 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -41,6 +41,7 @@ import android.os.Binder;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.SystemProperties;
import android.telephony.TelephonyManager;
@@ -872,6 +873,10 @@ public class Tethering extends BaseNetworkObserver {
if (tm != null) {
secureSetting = tm.getTetherApnRequired();
}
+ // Allow override of TETHER_DUN_REQUIRED via prop
+ int prop = SystemProperties.getInt("persist.sys.dun.override", -1);
+ secureSetting = ((prop < 3) && (prop >= 0)) ? prop : secureSetting;
+
synchronized (mPublicSync) {
// 2 = not set, 0 = DUN not required, 1 = DUN required
if (secureSetting != 2) {
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index e15bca6..575701a 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -22,6 +22,8 @@ import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
+import android.content.Context;
+import android.content.res.Resources;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@@ -193,11 +195,18 @@ class AutomaticBrightnessController {
private int mBrightnessAdjustmentSampleOldBrightness;
private float mBrightnessAdjustmentSampleOldGamma;
- public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
+ // Night mode color temperature adjustments
+ private final LiveDisplayController mLiveDisplay;
+
+ private final Context mContext;
+
+ public AutomaticBrightnessController(Context context, Callbacks callbacks, Looper looper,
SensorManager sensorManager, Spline autoBrightnessSpline, int lightSensorWarmUpTime,
int brightnessMin, int brightnessMax, float dozeScaleFactor,
int lightSensorRate, long brighteningLightDebounceConfig,
- long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig) {
+ long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig,
+ LiveDisplayController ldc) {
+ mContext = context;
mCallbacks = callbacks;
mTwilight = LocalServices.getService(TwilightManager.class);
mSensorManager = sensorManager;
@@ -210,6 +219,7 @@ class AutomaticBrightnessController {
mBrighteningLightDebounceConfig = brighteningLightDebounceConfig;
mDarkeningLightDebounceConfig = darkeningLightDebounceConfig;
mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig;
+ mLiveDisplay = ldc;
mHandler = new AutomaticBrightnessHandler(looper);
mAmbientLightRingBuffer = new AmbientLightRingBuffer(mLightSensorRate);
@@ -478,6 +488,9 @@ class AutomaticBrightnessController {
}
}
+ // Update LiveDisplay with the current lux
+ mLiveDisplay.updateLiveDisplay(mAmbientLux);
+
if (USE_TWILIGHT_ADJUSTMENT) {
TwilightState state = mTwilight.getCurrentState();
if (state != null && state.isNight()) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index b2ab797..81c3791 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -287,6 +287,8 @@ public final class DisplayManagerService extends SystemService {
mOnlyCore = onlyCore;
}
+ mDisplayPowerController.systemReady();
+
mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 452378f..45065b8 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -19,6 +19,7 @@ package com.android.server.display;
import com.android.internal.app.IBatteryStats;
import com.android.server.LocalServices;
import com.android.server.am.BatteryStatsService;
+import com.android.server.lights.LightsManager;
import android.animation.Animator;
import android.animation.ObjectAnimator;
@@ -124,6 +125,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// Battery stats.
private final IBatteryStats mBatteryStats;
+ // The lights service.
+ private final LightsManager mLights;
+
// The sensor manager.
private final SensorManager mSensorManager;
@@ -247,6 +251,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// The controller for the automatic brightness level.
private AutomaticBrightnessController mAutomaticBrightnessController;
+ // The controller for LiveDisplay
+ private final LiveDisplayController mLiveDisplayController;
+
// Animators.
private ObjectAnimator mColorFadeOnAnimator;
private ObjectAnimator mColorFadeOffAnimator;
@@ -262,11 +269,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mCallbacks = callbacks;
mBatteryStats = BatteryStatsService.getService();
+ mLights = LocalServices.getService(LightsManager.class);
mSensorManager = sensorManager;
mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class);
mBlanker = blanker;
mContext = context;
+ mLiveDisplayController = new LiveDisplayController(context, handler.getLooper());
+
final Resources resources = context.getResources();
final int screenBrightnessSettingMinimum = clampAbsoluteBrightness(resources.getInteger(
com.android.internal.R.integer.config_screenBrightnessSettingMinimum));
@@ -343,12 +353,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
if (bottom < screenBrightnessRangeMinimum) {
screenBrightnessRangeMinimum = bottom;
}
- mAutomaticBrightnessController = new AutomaticBrightnessController(this,
+ mAutomaticBrightnessController = new AutomaticBrightnessController(mContext, this,
handler.getLooper(), sensorManager, screenAutoBrightnessSpline,
lightSensorWarmUpTimeConfig, screenBrightnessRangeMinimum,
mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,
brighteningLightDebounce, darkeningLightDebounce,
- autoBrightnessResetAmbientLuxAfterWarmUp);
+ autoBrightnessResetAmbientLuxAfterWarmUp, mLiveDisplayController);
}
}
@@ -573,6 +583,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
} else {
setProximitySensorEnabled(false);
mWaitingForNegativeProximity = false;
+ mProximity = PROXIMITY_UNKNOWN;
}
if (mScreenOffBecauseOfProximity
&& mProximity != PROXIMITY_POSITIVE) {
@@ -595,6 +606,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// Use zero brightness when screen is off.
if (state == Display.STATE_OFF) {
brightness = PowerManager.BRIGHTNESS_OFF;
+ mLights.getLight(LightsManager.LIGHT_ID_BUTTONS).setBrightness(brightness);
+ mLights.getLight(LightsManager.LIGHT_ID_KEYBOARD).setBrightness(brightness);
+ }
+
+ // Disable button lights when dozing
+ if (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND) {
+ mLights.getLight(LightsManager.LIGHT_ID_BUTTONS).setBrightness(PowerManager.BRIGHTNESS_OFF);
+ mLights.getLight(LightsManager.LIGHT_ID_KEYBOARD).setBrightness(PowerManager.BRIGHTNESS_OFF);
}
// Configure auto-brightness.
@@ -692,6 +711,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
}
+ // Update LiveDisplay now
+ mLiveDisplayController.updateLiveDisplay();
+
// Determine whether the display is ready for use in the newly requested state.
// Note that we do not wait for the brightness ramp animation to complete before
// reporting the display is ready because we only need to ensure the screen is in the
@@ -1129,6 +1151,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mAutomaticBrightnessController.dump(pw);
}
+ mLiveDisplayController.dump(pw);
}
private static String proximityToString(int state) {
@@ -1190,6 +1213,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
return MathUtils.constrain(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
}
+ void systemReady() {
+ mLiveDisplayController.systemReady();
+ }
+
private final class DisplayControllerHandler extends Handler {
public DisplayControllerHandler(Looper looper) {
super(looper, null, true /*async*/);
diff --git a/services/core/java/com/android/server/display/LiveDisplayController.java b/services/core/java/com/android/server/display/LiveDisplayController.java
new file mode 100644
index 0000000..1ed81da
--- /dev/null
+++ b/services/core/java/com/android/server/display/LiveDisplayController.java
@@ -0,0 +1,823 @@
+/*
+ * 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 com.android.server.display;
+
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Parcel;
+import android.os.PowerManagerInternal;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.MathUtils;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
+import com.android.server.accessibility.DisplayAdjustmentUtils;
+import com.android.server.twilight.TwilightListener;
+import com.android.server.twilight.TwilightManager;
+import com.android.server.twilight.TwilightState;
+
+import cyanogenmod.hardware.CMHardwareManager;
+
+import java.io.PrintWriter;
+
+public class LiveDisplayController {
+
+ private static final String TAG = "LiveDisplay";
+
+ private static final long TWILIGHT_ADJUSTMENT_TIME = DateUtils.HOUR_IN_MILLIS * 1;
+
+ private static final int OFF_TEMPERATURE = 6500;
+
+ public static final int MODE_OFF = 0;
+ public static final int MODE_NIGHT = 1;
+ public static final int MODE_AUTO = 2;
+ public static final int MODE_OUTDOOR = 3;
+ public static final int MODE_DAY = 4;
+
+ private int mColorTemperature = OFF_TEMPERATURE;
+ private float mCurrentLux = 0.0f;
+
+ private int mHintCounter;
+ private int mMode;
+
+ private boolean mOutdoorMode;
+ private boolean mColorEnhancement;
+ private boolean mLowPower;
+
+ private final Context mContext;
+ private final Handler mHandler;
+
+ private CMHardwareManager mHardware;
+
+ private int mDayTemperature;
+ private int mNightTemperature;
+
+ private boolean mUseOutdoorMode;
+ private boolean mUseColorEnhancement;
+ private boolean mUseLowPower;
+
+ private final float[] mColorAdjustment = new float[] { 1.0f, 1.0f, 1.0f };
+ private final float[] mRGB = new float[] { 0.0f, 0.0f, 0.0f };
+
+ private TwilightManager mTwilightManager;
+ private boolean mSunset = false;
+
+ private SettingsObserver mObserver;
+
+ private ValueAnimator mAnimator;
+
+ private int mDefaultDayTemperature;
+ private int mDefaultNightTemperature;
+ private int mDefaultOutdoorLux;
+
+ private boolean mInitialized = false;
+
+ private static final int MSG_UPDATE_LIVE_DISPLAY = 1;
+
+ // Display postprocessing can have power impact. Disable it if powersave mode is on.
+ private boolean mLowPerformance = false;
+ private PowerManagerInternal.LowPowerModeListener mLowPowerModeListener =
+ new PowerManagerInternal.LowPowerModeListener() {
+ @Override
+ public void onLowPowerModeChanged(boolean enabled) {
+ mLowPerformance = enabled;
+ updateLiveDisplay(mCurrentLux);
+ }
+ };
+
+ LiveDisplayController(Context context, Looper looper) {
+ mContext = context;
+ mHandler = new LiveDisplayHandler(looper);
+ }
+
+ void systemReady() {
+ mHardware = CMHardwareManager.getInstance(mContext);
+
+ mDefaultDayTemperature = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_dayColorTemperature);
+ mDefaultNightTemperature = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_nightColorTemperature);
+ mDefaultOutdoorLux = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_outdoorAmbientLux);
+
+ // Counter used to determine when we should tell the user about this feature.
+ // If it's not used after 3 sunsets, we'll show the hint once.
+ mHintCounter = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.LIVE_DISPLAY_HINTED,
+ -3,
+ UserHandle.USER_CURRENT);
+
+ mUseOutdoorMode =
+ mHardware.isSupported(CMHardwareManager.FEATURE_SUNLIGHT_ENHANCEMENT);
+
+ mUseLowPower =
+ mHardware.isSupported(CMHardwareManager.FEATURE_ADAPTIVE_BACKLIGHT);
+ if (mUseLowPower) {
+ mLowPower = mHardware.get(CMHardwareManager.FEATURE_ADAPTIVE_BACKLIGHT);
+ }
+
+ mUseColorEnhancement =
+ mHardware.isSupported(CMHardwareManager.FEATURE_COLOR_ENHANCEMENT);
+ if (mUseColorEnhancement) {
+ mColorEnhancement =
+ mHardware.get(CMHardwareManager.FEATURE_COLOR_ENHANCEMENT);
+ }
+
+ updateSettings();
+
+ mObserver = new SettingsObserver();
+ mObserver.register(true);
+
+ PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
+ pmi.registerLowPowerModeObserver(mLowPowerModeListener);
+ mLowPerformance = pmi.getLowPowerModeEnabled();
+
+ mTwilightManager = LocalServices.getService(TwilightManager.class);
+ mTwilightManager.registerListener(mTwilightListener, mHandler);
+
+ mInitialized = true;
+ }
+
+ private void updateSettings() {
+ mDayTemperature = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.DISPLAY_TEMPERATURE_DAY,
+ mDefaultDayTemperature,
+ UserHandle.USER_CURRENT);
+ mNightTemperature = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.DISPLAY_TEMPERATURE_NIGHT,
+ mDefaultNightTemperature,
+ UserHandle.USER_CURRENT);
+ mMode = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.DISPLAY_TEMPERATURE_MODE,
+ MODE_OFF,
+ UserHandle.USER_CURRENT);
+
+ // Clear the hint forever
+ if (mMode != MODE_OFF) {
+ saveUserHint(1);
+ }
+
+ // Manual color adjustment will be set as a space separated string of float values
+ String colorAdjustmentTemp = Settings.System.getStringForUser(mContext.getContentResolver(),
+ Settings.System.DISPLAY_COLOR_ADJUSTMENT,
+ UserHandle.USER_CURRENT);
+ String[] colorAdjustment = colorAdjustmentTemp == null ?
+ null : colorAdjustmentTemp.split(" ");
+ if (colorAdjustment == null || colorAdjustment.length != 3) {
+ colorAdjustment = new String[] { "1.0", "1.0", "1.0" };
+ }
+ try {
+ mColorAdjustment[0] = Float.parseFloat(colorAdjustment[0]);
+ mColorAdjustment[1] = Float.parseFloat(colorAdjustment[1]);
+ mColorAdjustment[2] = Float.parseFloat(colorAdjustment[2]);
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, e.getMessage(), e);
+ mColorAdjustment[0] = 1.0f;
+ mColorAdjustment[1] = 1.0f;
+ mColorAdjustment[2] = 1.0f;
+ }
+
+ updateLiveDisplay(mCurrentLux);
+ }
+
+ private final class SettingsObserver extends ContentObserver {
+ private final Uri DISPLAY_TEMPERATURE_DAY_URI =
+ Settings.System.getUriFor(Settings.System.DISPLAY_TEMPERATURE_DAY);
+ private final Uri DISPLAY_TEMPERATURE_NIGHT_URI =
+ Settings.System.getUriFor(Settings.System.DISPLAY_TEMPERATURE_NIGHT);
+ private final Uri DISPLAY_TEMPERATURE_MODE_URI =
+ Settings.System.getUriFor(Settings.System.DISPLAY_TEMPERATURE_MODE);
+ private final Uri DISPLAY_AUTO_OUTDOOR_MODE_URI =
+ Settings.System.getUriFor(Settings.System.DISPLAY_AUTO_OUTDOOR_MODE);
+ private final Uri DISPLAY_LOW_POWER_URI =
+ Settings.System.getUriFor(Settings.System.DISPLAY_LOW_POWER);
+ private final Uri DISPLAY_COLOR_ENHANCE_URI =
+ Settings.System.getUriFor(Settings.System.DISPLAY_COLOR_ENHANCE);
+ private final Uri DISPLAY_COLOR_ADJUSTMENT_URI =
+ Settings.System.getUriFor(Settings.System.DISPLAY_COLOR_ADJUSTMENT);
+ public SettingsObserver() {
+ super(mHandler);
+ }
+
+ public void register(boolean register) {
+ final ContentResolver cr = mContext.getContentResolver();
+ if (register) {
+ cr.registerContentObserver(DISPLAY_TEMPERATURE_DAY_URI, false, this, UserHandle.USER_ALL);
+ cr.registerContentObserver(DISPLAY_TEMPERATURE_NIGHT_URI, false, this, UserHandle.USER_ALL);
+ cr.registerContentObserver(DISPLAY_TEMPERATURE_MODE_URI, false, this, UserHandle.USER_ALL);
+ cr.registerContentObserver(DISPLAY_AUTO_OUTDOOR_MODE_URI, false, this, UserHandle.USER_ALL);
+ cr.registerContentObserver(DISPLAY_LOW_POWER_URI, false, this, UserHandle.USER_ALL);
+ cr.registerContentObserver(DISPLAY_COLOR_ENHANCE_URI, false, this, UserHandle.USER_ALL);
+ cr.registerContentObserver(DISPLAY_COLOR_ADJUSTMENT_URI, false, this, UserHandle.USER_ALL);
+ } else {
+ cr.unregisterContentObserver(this);
+ }
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+ updateSettings();
+ }
+ }
+
+ public void updateLiveDisplay() {
+ updateLiveDisplay(mCurrentLux);
+ }
+
+ synchronized void updateLiveDisplay(float lux) {
+ mCurrentLux = lux;
+ mHandler.removeMessages(MSG_UPDATE_LIVE_DISPLAY);
+ mHandler.sendEmptyMessage(MSG_UPDATE_LIVE_DISPLAY);
+ }
+
+ private synchronized void updateColorTemperature(TwilightState twilight) {
+ int temperature = mDayTemperature;
+ if (mMode == MODE_OFF || mLowPerformance) {
+ temperature = OFF_TEMPERATURE;
+ } else if (mMode == MODE_NIGHT) {
+ temperature = mNightTemperature;
+ } else if (mMode == MODE_AUTO) {
+ temperature = getTwilightK(twilight);
+ }
+
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ }
+ mAnimator = ValueAnimator.ofInt(mColorTemperature, temperature);
+ mAnimator.setDuration(Math.abs(mColorTemperature - temperature) / 2);
+ mAnimator.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ setDisplayTemperature((Integer)animation.getAnimatedValue());
+ }
+ });
+ mAnimator.start();
+ }
+
+ private synchronized void setDisplayTemperature(int temperature) {
+ mColorTemperature = temperature;
+
+ final float[] rgb = temperatureToRGB(temperature);
+
+ if (!mLowPerformance) {
+ rgb[0] *= mColorAdjustment[0];
+ rgb[1] *= mColorAdjustment[1];
+ rgb[2] *= mColorAdjustment[2];
+ }
+
+ if (rgb[0] == mRGB[0] && rgb[1] == mRGB[1] && rgb[2] == mRGB[2]) {
+ // no changes
+ return;
+ }
+
+ System.arraycopy(rgb, 0, mRGB, 0, 3);
+
+ Slog.d(TAG, "Adjust display temperature to " + temperature +
+ "K [r=" + rgb[0] + " g=" + rgb[1] + " b=" + rgb[2] + "]");
+
+ if (mHardware.isSupported(CMHardwareManager.FEATURE_DISPLAY_COLOR_CALIBRATION)) {
+ // Clear this out in case of an upgrade
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.LIVE_DISPLAY_COLOR_MATRIX,
+ null,
+ UserHandle.USER_CURRENT);
+
+ int max = mHardware.getDisplayColorCalibrationMax();
+ mHardware.setDisplayColorCalibration(new int[] {
+ (int) Math.ceil(rgb[0] * max),
+ (int) Math.ceil(rgb[1] * max),
+ (int) Math.ceil(rgb[2] * max)
+ });
+ screenRefresh();
+ } else {
+ String colorMatrixStr = null;
+ if (rgb[0] != 1.0f || rgb[1] != 1.0f || rgb[2] != 1.0f) {
+ final Float[] colorMatrix = new Float[] {
+ rgb[0], 0.0f, 0.0f, 0.0f,
+ 0.0f, rgb[1], 0.0f, 0.0f,
+ 0.0f, 0.0f, rgb[2], 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f };
+ colorMatrixStr = TextUtils.join(" ", colorMatrix);
+ }
+
+ // For GPU color transform, go thru DisplayAdjustmentUtils in
+ // order to coexist with accessibility settings
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.LIVE_DISPLAY_COLOR_MATRIX,
+ colorMatrixStr,
+ UserHandle.USER_CURRENT);
+
+ DisplayAdjustmentUtils.applyAdjustments(mContext, UserHandle.USER_CURRENT);
+ }
+ }
+
+ /**
+ * Outdoor mode is optionally enabled when ambient lux > 10000 and it's daytime
+ * Melt faces!
+ *
+ * TODO: Use the camera or RGB sensor to determine if it's really sunlight
+ */
+ private synchronized void updateOutdoorMode(TwilightState twilight) {
+ if (!mUseOutdoorMode) {
+ return;
+ }
+
+ boolean value = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.DISPLAY_AUTO_OUTDOOR_MODE,
+ 1,
+ UserHandle.USER_CURRENT) == 1;
+
+ boolean enabled = !mLowPerformance &&
+ ((mMode == MODE_OUTDOOR) ||
+ (value && mMode == MODE_AUTO &&
+ twilight != null && !twilight.isNight() &&
+ mCurrentLux > mDefaultOutdoorLux));
+
+ if (enabled == mOutdoorMode) {
+ return;
+ }
+
+ mHardware.set(CMHardwareManager.FEATURE_SUNLIGHT_ENHANCEMENT, enabled);
+ mOutdoorMode = enabled;
+ }
+
+ /**
+ * Color enhancement is optional, but can look bad with night mode
+ */
+ private synchronized void updateColorEnhancement(TwilightState twilight) {
+ if (!mUseColorEnhancement) {
+ return;
+ }
+
+ boolean value = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.DISPLAY_COLOR_ENHANCE,
+ 1,
+ UserHandle.USER_CURRENT) == 1;
+
+ boolean enabled = !mLowPerformance && value &&
+ !(mMode == MODE_NIGHT ||
+ (mMode == MODE_AUTO && twilight != null && twilight.isNight()));
+
+ if (enabled == mColorEnhancement) {
+ return;
+ }
+
+ mHardware.set(CMHardwareManager.FEATURE_COLOR_ENHANCEMENT, enabled);
+ mColorEnhancement = enabled;
+ }
+
+ /**
+ * Adaptive backlight / low power mode. Turn it off when under very bright light.
+ */
+ private synchronized void updateLowPowerMode() {
+ if (!mUseLowPower) {
+ return;
+ }
+
+ boolean value = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.DISPLAY_LOW_POWER,
+ 1,
+ UserHandle.USER_CURRENT) == 1;
+
+ boolean enabled = value && (mCurrentLux < mDefaultOutdoorLux);
+
+ if (enabled == mLowPower) {
+ return;
+ }
+
+ mHardware.set(CMHardwareManager.FEATURE_ADAPTIVE_BACKLIGHT, enabled);
+ mLowPower = enabled;
+ }
+
+ /**
+ * Convert a color temperature value (in Kelvin) to a RGB units as floats.
+ * This can be used in a transform matrix or hardware gamma control.
+ *
+ * @param tempK
+ * @return
+ */
+ private static float[] temperatureToRGB(int degreesK) {
+ int k = MathUtils.constrain(degreesK, 1000, 20000);
+ float a = (k % 100) / 100.0f;
+ int i = ((k - 1000)/ 100) * 3;
+
+ return new float[] { interp(i, a), interp(i+1, a), interp(i+2, a) };
+ }
+
+ private static float interp(int i, float a) {
+ return MathUtils.lerp((float)sColorTable[i], (float)sColorTable[i+3], a);
+ }
+
+ /**
+ * Where is the sun anyway? This calculation determines day or night, and scales
+ * the value around sunset/sunrise for a smooth transition.
+ *
+ * @param now
+ * @param sunset
+ * @param sunrise
+ * @return float between 0 and 1
+ */
+ private static float adj(long now, long sunset, long sunrise) {
+ if (sunset < 0 || sunrise < 0
+ || now < sunset || now > sunrise) {
+ return 1.0f;
+ }
+
+ if (now < sunset + TWILIGHT_ADJUSTMENT_TIME) {
+ return MathUtils.lerp(1.0f, 0.0f,
+ (float)(now - sunset) / TWILIGHT_ADJUSTMENT_TIME);
+ }
+
+ if (now > sunrise - TWILIGHT_ADJUSTMENT_TIME) {
+ return MathUtils.lerp(1.0f, 0.0f,
+ (float)(sunrise - now) / TWILIGHT_ADJUSTMENT_TIME);
+ }
+
+ return 0.0f;
+ }
+
+ /**
+ * Determine the color temperature we should use for the display based on
+ * the position of the sun.
+ *
+ * @param state
+ * @return color temperature in Kelvin
+ */
+ private int getTwilightK(TwilightState state) {
+ float adjustment = 1.0f;
+
+ if (state != null) {
+ final long now = System.currentTimeMillis();
+ adjustment = adj(now, state.getYesterdaySunset(), state.getTodaySunrise()) *
+ adj(now, state.getTodaySunset(), state.getTomorrowSunrise());
+ }
+
+ return (int)MathUtils.lerp(mNightTemperature, mDayTemperature, adjustment);
+ }
+
+ /**
+ * Tell SurfaceFlinger to repaint the screen. This is called after updating
+ * hardware registers for display calibration to have an immediate effect.
+ */
+ private static void screenRefresh() {
+ try {
+ final IBinder flinger = ServiceManager.getService("SurfaceFlinger");
+ if (flinger != null) {
+ final Parcel data = Parcel.obtain();
+ data.writeInterfaceToken("android.ui.ISurfaceComposer");
+ flinger.transact(1004, data, null, 0);
+ data.recycle();
+ }
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Failed to refresh screen", ex);
+ }
+ }
+
+ private void saveUserHint(int value) {
+ if (mHintCounter == value) {
+ return;
+ }
+ Settings.System.putIntForUser(mContext.getContentResolver(),
+ Settings.System.LIVE_DISPLAY_HINTED,
+ value,
+ UserHandle.USER_CURRENT);
+ mHintCounter = value;
+ }
+
+ /**
+ * Show a friendly notification to the user about the potential benefits of decreasing
+ * blue light at night. Do this only once if the feature has not been used after
+ * three sunsets. It would be great to enable this by default, but we don't want
+ * the change of screen color to be considered a "bug" by a user who doesn't
+ * understand what's happening.
+ *
+ * @param state
+ */
+ private void updateUserHint(TwilightState state) {
+ // check if we should send the hint only once after sunset
+ if (state == null || mHintCounter == 1) {
+ return;
+ }
+ boolean transition = state.isNight() && !mSunset;
+ mSunset = state.isNight();
+ if (!transition) {
+ return;
+ }
+
+ if (mHintCounter <= 0) {
+ mHintCounter++;
+ saveUserHint(mHintCounter);
+ }
+ if (mHintCounter == 0) {
+ //show the notification and don't come back here
+ final Intent intent = new Intent("android.settings.LIVEDISPLAY_SETTINGS");
+ PendingIntent result = PendingIntent.getActivity(
+ mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ Notification.Builder builder = new Notification.Builder(mContext)
+ .setContentTitle(mContext.getResources().getString(
+ com.android.internal.R.string.live_display_title))
+ .setContentText(mContext.getResources().getString(
+ com.android.internal.R.string.live_display_hint))
+ .setSmallIcon(com.android.internal.R.drawable.ic_livedisplay_notif)
+ .setStyle(new Notification.BigTextStyle().bigText(mContext.getResources()
+ .getString(com.android.internal.R.string.live_display_hint)))
+ .setContentIntent(result);
+
+ NotificationManager nm =
+ (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ nm.notifyAsUser(null, 1, builder.build(), UserHandle.CURRENT);
+
+ saveUserHint(1);
+ }
+ }
+
+ private final TwilightListener mTwilightListener = new TwilightListener() {
+ @Override
+ public void onTwilightStateChanged() {
+ updateLiveDisplay(mCurrentLux);
+ }
+ };
+
+ private final class LiveDisplayHandler extends Handler {
+ public LiveDisplayHandler(Looper looper) {
+ super(looper, null, true /*async*/);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_LIVE_DISPLAY:
+ if (!mInitialized) {
+ break;
+ }
+ TwilightState twilight = mTwilightManager.getCurrentState();
+
+ updateColorTemperature(twilight);
+ updateOutdoorMode(twilight);
+ updateColorEnhancement(twilight);
+ updateLowPowerMode();
+ updateUserHint(twilight);
+
+ boolean transition = mMode == MODE_AUTO &&
+ mColorTemperature != mDayTemperature &&
+ mColorTemperature != mNightTemperature;
+ if (transition) {
+ // fire again in a minute
+ sendEmptyMessageDelayed(MSG_UPDATE_LIVE_DISPLAY,
+ DateUtils.MINUTE_IN_MILLIS);
+ }
+ break;
+ }
+ }
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println();
+ pw.println("LiveDisplay Controller Configuration:");
+ pw.println(" mDayTemperature=" + mDayTemperature);
+ pw.println(" mNightTemperature=" + mNightTemperature);
+ pw.println();
+ pw.println("LiveDisplay Controller State:");
+ pw.println(" mMode=" + (mLowPerformance ? "disabled in powersave mode" : mMode));
+ pw.println(" mSunset=" + mSunset);
+ pw.println(" mColorTemperature=" + mColorTemperature);
+ pw.println(" mColorAdjustment=[r: " + mColorAdjustment[0] + " g:" + mColorAdjustment[1] +
+ " b:" + mColorAdjustment[2] + "]");
+ pw.println(" mRGB=[r:" + mRGB[0] + " g:" + mRGB[1] + " b:" + mRGB[2] + "]");
+ pw.println(" mOutdoorMode=" + (mUseOutdoorMode ? mOutdoorMode : "N/A"));
+ pw.println(" mColorEnhancement=" + (mUseColorEnhancement ? mColorEnhancement : "N/A"));
+ pw.println(" mLowPower=" + (mUseLowPower ? mLowPower : "N/A"));
+ }
+
+ /**
+ * This table is a modified version of the original blackbody chart, found here:
+ * http://www.vendian.org/mncharity/dir3/blackbody/UnstableURLs/bbr_color.html
+ *
+ * Created by Ingo Thiel.
+ */
+ private static final double[] sColorTable = new double[] {
+ 1.00000000, 0.18172716, 0.00000000,
+ 1.00000000, 0.25503671, 0.00000000,
+ 1.00000000, 0.30942099, 0.00000000,
+ 1.00000000, 0.35357379, 0.00000000,
+ 1.00000000, 0.39091524, 0.00000000,
+ 1.00000000, 0.42322816, 0.00000000,
+ 1.00000000, 0.45159884, 0.00000000,
+ 1.00000000, 0.47675916, 0.00000000,
+ 1.00000000, 0.49923747, 0.00000000,
+ 1.00000000, 0.51943421, 0.00000000,
+ 1.00000000, 0.54360078, 0.08679949,
+ 1.00000000, 0.56618736, 0.14065513,
+ 1.00000000, 0.58734976, 0.18362641,
+ 1.00000000, 0.60724493, 0.22137978,
+ 1.00000000, 0.62600248, 0.25591950,
+ 1.00000000, 0.64373109, 0.28819679,
+ 1.00000000, 0.66052319, 0.31873863,
+ 1.00000000, 0.67645822, 0.34786758,
+ 1.00000000, 0.69160518, 0.37579588,
+ 1.00000000, 0.70602449, 0.40267128,
+ 1.00000000, 0.71976951, 0.42860152,
+ 1.00000000, 0.73288760, 0.45366838,
+ 1.00000000, 0.74542112, 0.47793608,
+ 1.00000000, 0.75740814, 0.50145662,
+ 1.00000000, 0.76888303, 0.52427322,
+ 1.00000000, 0.77987699, 0.54642268,
+ 1.00000000, 0.79041843, 0.56793692,
+ 1.00000000, 0.80053332, 0.58884417,
+ 1.00000000, 0.81024551, 0.60916971,
+ 1.00000000, 0.81957693, 0.62893653,
+ 1.00000000, 0.82854786, 0.64816570,
+ 1.00000000, 0.83717703, 0.66687674,
+ 1.00000000, 0.84548188, 0.68508786,
+ 1.00000000, 0.85347859, 0.70281616,
+ 1.00000000, 0.86118227, 0.72007777,
+ 1.00000000, 0.86860704, 0.73688797,
+ 1.00000000, 0.87576611, 0.75326132,
+ 1.00000000, 0.88267187, 0.76921169,
+ 1.00000000, 0.88933596, 0.78475236,
+ 1.00000000, 0.89576933, 0.79989606,
+ 1.00000000, 0.90198230, 0.81465502,
+ 1.00000000, 0.90963069, 0.82838210,
+ 1.00000000, 0.91710889, 0.84190889,
+ 1.00000000, 0.92441842, 0.85523742,
+ 1.00000000, 0.93156127, 0.86836903,
+ 1.00000000, 0.93853986, 0.88130458,
+ 1.00000000, 0.94535695, 0.89404470,
+ 1.00000000, 0.95201559, 0.90658983,
+ 1.00000000, 0.95851906, 0.91894041,
+ 1.00000000, 0.96487079, 0.93109690,
+ 1.00000000, 0.97107439, 0.94305985,
+ 1.00000000, 0.97713351, 0.95482993,
+ 1.00000000, 0.98305189, 0.96640795,
+ 1.00000000, 0.98883326, 0.97779486,
+ 1.00000000, 0.99448139, 0.98899179,
+ 1.00000000, 1.00000000, 1.00000000,
+ 0.98947904, 0.99348723, 1.00000000,
+ 0.97940448, 0.98722715, 1.00000000,
+ 0.96975025, 0.98120637, 1.00000000,
+ 0.96049223, 0.97541240, 1.00000000,
+ 0.95160805, 0.96983355, 1.00000000,
+ 0.94303638, 0.96443333, 1.00000000,
+ 0.93480451, 0.95923080, 1.00000000,
+ 0.92689056, 0.95421394, 1.00000000,
+ 0.91927697, 0.94937330, 1.00000000,
+ 0.91194747, 0.94470005, 1.00000000,
+ 0.90488690, 0.94018594, 1.00000000,
+ 0.89808115, 0.93582323, 1.00000000,
+ 0.89151710, 0.93160469, 1.00000000,
+ 0.88518247, 0.92752354, 1.00000000,
+ 0.87906581, 0.92357340, 1.00000000,
+ 0.87315640, 0.91974827, 1.00000000,
+ 0.86744421, 0.91604254, 1.00000000,
+ 0.86191983, 0.91245088, 1.00000000,
+ 0.85657444, 0.90896831, 1.00000000,
+ 0.85139976, 0.90559011, 1.00000000,
+ 0.84638799, 0.90231183, 1.00000000,
+ 0.84153180, 0.89912926, 1.00000000,
+ 0.83682430, 0.89603843, 1.00000000,
+ 0.83225897, 0.89303558, 1.00000000,
+ 0.82782969, 0.89011714, 1.00000000,
+ 0.82353066, 0.88727974, 1.00000000,
+ 0.81935641, 0.88452017, 1.00000000,
+ 0.81530175, 0.88183541, 1.00000000,
+ 0.81136180, 0.87922257, 1.00000000,
+ 0.80753191, 0.87667891, 1.00000000,
+ 0.80380769, 0.87420182, 1.00000000,
+ 0.80018497, 0.87178882, 1.00000000,
+ 0.79665980, 0.86943756, 1.00000000,
+ 0.79322843, 0.86714579, 1.00000000,
+ 0.78988728, 0.86491137, 1.00000000,
+ 0.78663296, 0.86273225, 1.00000000,
+ 0.78346225, 0.86060650, 1.00000000,
+ 0.78037207, 0.85853224, 1.00000000,
+ 0.77735950, 0.85650771, 1.00000000,
+ 0.77442176, 0.85453121, 1.00000000,
+ 0.77155617, 0.85260112, 1.00000000,
+ 0.76876022, 0.85071588, 1.00000000,
+ 0.76603147, 0.84887402, 1.00000000,
+ 0.76336762, 0.84707411, 1.00000000,
+ 0.76076645, 0.84531479, 1.00000000,
+ 0.75822586, 0.84359476, 1.00000000,
+ 0.75574383, 0.84191277, 1.00000000,
+ 0.75331843, 0.84026762, 1.00000000,
+ 0.75094780, 0.83865816, 1.00000000,
+ 0.74863017, 0.83708329, 1.00000000,
+ 0.74636386, 0.83554194, 1.00000000,
+ 0.74414722, 0.83403311, 1.00000000,
+ 0.74197871, 0.83255582, 1.00000000,
+ 0.73985682, 0.83110912, 1.00000000,
+ 0.73778012, 0.82969211, 1.00000000,
+ 0.73574723, 0.82830393, 1.00000000,
+ 0.73375683, 0.82694373, 1.00000000,
+ 0.73180765, 0.82561071, 1.00000000,
+ 0.72989845, 0.82430410, 1.00000000,
+ 0.72802807, 0.82302316, 1.00000000,
+ 0.72619537, 0.82176715, 1.00000000,
+ 0.72439927, 0.82053539, 1.00000000,
+ 0.72263872, 0.81932722, 1.00000000,
+ 0.72091270, 0.81814197, 1.00000000,
+ 0.71922025, 0.81697905, 1.00000000,
+ 0.71756043, 0.81583783, 1.00000000,
+ 0.71593234, 0.81471775, 1.00000000,
+ 0.71433510, 0.81361825, 1.00000000,
+ 0.71276788, 0.81253878, 1.00000000,
+ 0.71122987, 0.81147883, 1.00000000,
+ 0.70972029, 0.81043789, 1.00000000,
+ 0.70823838, 0.80941546, 1.00000000,
+ 0.70678342, 0.80841109, 1.00000000,
+ 0.70535469, 0.80742432, 1.00000000,
+ 0.70395153, 0.80645469, 1.00000000,
+ 0.70257327, 0.80550180, 1.00000000,
+ 0.70121928, 0.80456522, 1.00000000,
+ 0.69988894, 0.80364455, 1.00000000,
+ 0.69858167, 0.80273941, 1.00000000,
+ 0.69729688, 0.80184943, 1.00000000,
+ 0.69603402, 0.80097423, 1.00000000,
+ 0.69479255, 0.80011347, 1.00000000,
+ 0.69357196, 0.79926681, 1.00000000,
+ 0.69237173, 0.79843391, 1.00000000,
+ 0.69119138, 0.79761446, 1.00000000,
+ 0.69003044, 0.79680814, 1.00000000,
+ 0.68888844, 0.79601466, 1.00000000,
+ 0.68776494, 0.79523371, 1.00000000,
+ 0.68665951, 0.79446502, 1.00000000,
+ 0.68557173, 0.79370830, 1.00000000,
+ 0.68450119, 0.79296330, 1.00000000,
+ 0.68344751, 0.79222975, 1.00000000,
+ 0.68241029, 0.79150740, 1.00000000,
+ 0.68138918, 0.79079600, 1.00000000,
+ 0.68038380, 0.79009531, 1.00000000,
+ 0.67939381, 0.78940511, 1.00000000,
+ 0.67841888, 0.78872517, 1.00000000,
+ 0.67745866, 0.78805526, 1.00000000,
+ 0.67651284, 0.78739518, 1.00000000,
+ 0.67558112, 0.78674472, 1.00000000,
+ 0.67466317, 0.78610368, 1.00000000,
+ 0.67375872, 0.78547186, 1.00000000,
+ 0.67286748, 0.78484907, 1.00000000,
+ 0.67198916, 0.78423512, 1.00000000,
+ 0.67112350, 0.78362984, 1.00000000,
+ 0.67027024, 0.78303305, 1.00000000,
+ 0.66942911, 0.78244457, 1.00000000,
+ 0.66859988, 0.78186425, 1.00000000,
+ 0.66778228, 0.78129191, 1.00000000,
+ 0.66697610, 0.78072740, 1.00000000,
+ 0.66618110, 0.78017057, 1.00000000,
+ 0.66539706, 0.77962127, 1.00000000,
+ 0.66462376, 0.77907934, 1.00000000,
+ 0.66386098, 0.77854465, 1.00000000,
+ 0.66310852, 0.77801705, 1.00000000,
+ 0.66236618, 0.77749642, 1.00000000,
+ 0.66163375, 0.77698261, 1.00000000,
+ 0.66091106, 0.77647551, 1.00000000,
+ 0.66019791, 0.77597498, 1.00000000,
+ 0.65949412, 0.77548090, 1.00000000,
+ 0.65879952, 0.77499315, 1.00000000,
+ 0.65811392, 0.77451161, 1.00000000,
+ 0.65743716, 0.77403618, 1.00000000,
+ 0.65676908, 0.77356673, 1.00000000,
+ 0.65610952, 0.77310316, 1.00000000,
+ 0.65545831, 0.77264537, 1.00000000,
+ 0.65481530, 0.77219324, 1.00000000,
+ 0.65418036, 0.77174669, 1.00000000,
+ 0.65355332, 0.77130560, 1.00000000,
+ 0.65293404, 0.77086988, 1.00000000,
+ 0.65232240, 0.77043944, 1.00000000,
+ 0.65171824, 0.77001419, 1.00000000,
+ 0.65112144, 0.76959404, 1.00000000,
+ 0.65053187, 0.76917889, 1.00000000,
+ 0.64994941, 0.76876866, 1.00000000,
+ 0.64937392, 0.76836326, 1.00000000
+ };
+}
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 8813a61..7d1dbe1 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -18,6 +18,7 @@ package com.android.server.dreams;
import static android.Manifest.permission.BIND_DREAM_SERVICE;
+import android.view.WindowManagerPolicy;
import com.android.internal.util.DumpUtils;
import com.android.server.FgThread;
import com.android.server.SystemService;
@@ -84,6 +85,7 @@ public final class DreamManagerService extends SystemService {
private boolean mCurrentDreamIsWaking;
private int mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN;
private int mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+ private int mLidState = WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
public DreamManagerService(Context context) {
super(context);
@@ -150,6 +152,12 @@ public final class DreamManagerService extends SystemService {
}
}
+ private boolean isDozingInternal() {
+ synchronized (mLock) {
+ return mCurrentDreamIsDozing;
+ }
+ }
+
private void requestDreamInternal() {
// Ask the power manager to nap. It will eventually call back into
// startDream() if/when it is appropriate to start dreaming.
@@ -219,7 +227,8 @@ public final class DreamManagerService extends SystemService {
}
synchronized (mLock) {
- if (mCurrentDreamToken == token && mCurrentDreamCanDoze) {
+ if (mCurrentDreamToken == token && mCurrentDreamCanDoze
+ && mLidState != WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED) {
mCurrentDreamDozeScreenState = screenState;
mCurrentDreamDozeScreenBrightness = screenBrightness;
mPowerManagerInternal.setDozeOverrideFromDreamManager(
@@ -232,6 +241,40 @@ public final class DreamManagerService extends SystemService {
}
}
+ private int getLidStateInternal() {
+ return mLidState;
+ }
+
+ private void setLidStateInternal(int state) {
+ synchronized (mLock) {
+ mLidState = state;
+ }
+ switch (state) {
+ case WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT:
+ // do nothing
+ break;
+ case WindowManagerPolicy.WindowManagerFuncs.LID_OPEN:
+ synchronized (mLock) {
+ mPowerManagerInternal.setDozeOverrideFromDreamManager(
+ Display.STATE_UNKNOWN, PowerManager.BRIGHTNESS_DEFAULT);
+ }
+ break;
+ case WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED:
+ // mimicing logic from stopDozingInternal(), stop any thing when the lid is closed.
+ synchronized (mLock) {
+ if (mCurrentDreamIsDozing) {
+ mCurrentDreamIsDozing = false;
+ if (mDozeWakeLock.isHeld()) {
+ mDozeWakeLock.release();
+ }
+ mPowerManagerInternal.setDozeOverrideFromDreamManager(
+ Display.STATE_OFF, PowerManager.BRIGHTNESS_OFF);
+ }
+ }
+ break;
+ }
+ }
+
private void stopDozingInternal(IBinder token) {
if (DEBUG) {
Slog.d(TAG, "Dream requested to stop dozing: " + token);
@@ -530,6 +573,18 @@ public final class DreamManagerService extends SystemService {
}
@Override // Binder call
+ public boolean isDozing() {
+ checkPermission(android.Manifest.permission.READ_DREAM_STATE);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return isDozingInternal();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
public void dream() {
checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
@@ -621,6 +676,30 @@ public final class DreamManagerService extends SystemService {
Binder.restoreCallingIdentity(ident);
}
}
+
+ @Override
+ public void setLidState(int lidState) {
+ checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ setLidStateInternal(lidState);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public int getLidState() {
+ checkPermission(Manifest.permission.READ_DREAM_STATE);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return getLidStateInternal();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
private final class LocalService extends DreamManagerInternal {
@@ -638,6 +717,11 @@ public final class DreamManagerService extends SystemService {
public boolean isDreaming() {
return isDreamingInternal();
}
+
+ @Override
+ public boolean isDozing() {
+ return isDozingInternal();
+ }
}
private final Runnable mSystemPropertiesChanged = new Runnable() {
diff --git a/services/core/java/com/android/server/gesture/GestureInputFilter.java b/services/core/java/com/android/server/gesture/GestureInputFilter.java
new file mode 100644
index 0000000..e40761f
--- /dev/null
+++ b/services/core/java/com/android/server/gesture/GestureInputFilter.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2013 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 com.android.server.gesture;
+
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.content.Context;
+import android.hardware.input.InputManager;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.view.Display;
+import android.view.GestureDetector;
+import android.view.GestureDetector.OnDoubleTapListener;
+import android.view.IInputFilter;
+import android.view.IInputFilterHost;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.MotionEvent;
+import android.view.OrientationEventListener;
+import android.view.ViewConfiguration;
+import android.view.WindowManager;
+import java.io.PrintWriter;
+
+/**
+ * A simple input filter that listens for gesture sensor events and converts
+ * them to input events to be injected into the input stream.
+ */
+public class GestureInputFilter implements IInputFilter, GestureDetector.OnGestureListener, OnDoubleTapListener {
+
+ private static final String TAG = "GestureInputFilter";
+ private static final boolean DEBUG = false;
+
+ private IInputFilterHost mHost = null;
+
+ private GestureDetector mGestureDetector;
+ private InputManager mInputManager;
+ private OrientationEventListener mOrientationListener;
+ private final int mScreenWidth, mScreenHeight;
+ private float mGesturePadWidth, mGesturePadHeight;
+ private int mTouchSlop, mOrientation;
+ private Context mContext;
+ private PendingIntent mLongPressPendingIntent;
+ private PendingIntent mDoubleClickPendingIntent;
+
+ public GestureInputFilter(Context context) {
+ mInputManager = InputManager.getInstance();
+ mContext = context;
+ for (int id : mInputManager.getInputDeviceIds()) {
+ InputDevice inputDevice = mInputManager.getInputDevice(id);
+ if ((inputDevice.getSources() & InputDevice.SOURCE_GESTURE_SENSOR)
+ == mInputManager.getInputDevice(id).getSources()) {
+ mGesturePadWidth = inputDevice.getMotionRange(MotionEvent.AXIS_X).getMax();
+ mGesturePadHeight = inputDevice.getMotionRange(MotionEvent.AXIS_Y).getMax();
+ break;
+ }
+ }
+ ViewConfiguration vc = ViewConfiguration.get(context);
+ mTouchSlop = vc.getScaledTouchSlop();
+ WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ Display display = wm.getDefaultDisplay();
+ mScreenWidth = display.getWidth();
+ mScreenHeight = display.getHeight();
+ mGestureDetector = new GestureDetector(context, this);
+ mGestureDetector.setOnDoubleTapListener(this);
+ mOrientationListener = new OrientationEventListener(context) {
+ @Override
+ public void onOrientationChanged(int orientation) {
+ if (orientation == -1) {
+ return;
+ }
+ mOrientation = (orientation + 45) / 90 * 90;
+ }
+ };
+ }
+
+ /**
+ * Called to enqueue the input event for filtering.
+ * The event must be recycled after the input filter processed it.
+ * This method is guaranteed to be non-reentrant.
+ *
+ * @see InputFilter#filterInputEvent(InputEvent, int)
+ * @param event The input event to enqueue.
+ */
+ // called by the input dispatcher thread
+ public void filterInputEvent(InputEvent event, int policyFlags)
+ throws RemoteException {
+ if (DEBUG) Slog.d(TAG, event.toString());
+
+ try {
+ if (event.getSource() != InputDevice.SOURCE_GESTURE_SENSOR
+ || !(event instanceof MotionEvent)) {
+ try {
+ mHost.sendInputEvent(event, policyFlags);
+ } catch (RemoteException e) {
+ /* ignore */
+ }
+ return;
+ }
+
+ MotionEvent motionEvent = (MotionEvent) event;
+ mGestureDetector.onTouchEvent(motionEvent);
+ } finally {
+ event.recycle();
+ }
+ }
+
+ // called by the input dispatcher thread
+ public void install(IInputFilterHost host) throws RemoteException {
+ if (DEBUG) {
+ Slog.d(TAG, "Gesture input filter installed.");
+ }
+ mHost = host;
+ mOrientationListener.enable();
+ }
+
+ // called by the input dispatcher thread
+ public void uninstall() throws RemoteException {
+ if (DEBUG) {
+ Slog.d(TAG, "Gesture input filter uninstalled.");
+ }
+ mHost = null;
+ mOrientationListener.disable();
+ mContext = null;
+ }
+
+ // should never be called
+ public IBinder asBinder() {
+ throw new UnsupportedOperationException();
+ }
+
+ // called by a Binder thread
+ public void dump(PrintWriter pw, String prefix) {
+
+ }
+
+ private boolean generateSwipe(MotionEvent e1, MotionEvent e2) {
+ switch (mOrientation) {
+ case 90:
+ Slog.d(TAG, "Adjusting motion for 90 degrees");
+ e1.setLocation(e1.getY(), e1.getX());
+ e2.setLocation(e2.getY(), e2.getX());
+ break;
+ case 180:
+ Slog.d(TAG, "Adjusting motion for 180 degrees");
+ e1.setLocation(mGesturePadWidth - e1.getX(),
+ mGesturePadHeight - e1.getY());
+ e2.setLocation(mGesturePadWidth - e2.getX(),
+ mGesturePadHeight - e2.getY());
+ break;
+ case 270:
+ Slog.d(TAG, "Adjusting motion for 270 degrees");
+ e1.setLocation(mGesturePadHeight - e1.getY(),
+ e1.getX());
+ e2.setLocation(mGesturePadHeight - e2.getY(),
+ e2.getX());
+ break;
+ }
+
+ float deltaX = Math.abs(e1.getX() - e2.getX());
+ float deltaY = Math.abs(e1.getY() - e2.getY());
+
+ if (deltaX < mTouchSlop && deltaY < mTouchSlop) {
+ return false;
+ }
+
+ if (deltaX > deltaY) {
+ e2.setLocation(e2.getX(), e1.getY());
+ } else if (deltaY > deltaX) {
+ e2.setLocation(e1.getX(), e2.getY());
+ }
+
+ float scaleX = mScreenWidth / mGesturePadWidth;
+ float scaleY = mScreenHeight / mGesturePadHeight;
+
+ float magnitudeX = deltaX * scaleX;
+ float magnitudeY = deltaY * scaleY;
+
+ float origX = mScreenWidth / 2;
+ float origY = mScreenHeight / 2;
+ float endX = 0.0f;
+ float endY = 0.0f;
+
+ if (e2.getY() > e1.getY()) {
+ if (DEBUG) Slog.d(TAG, "Detected down motion");
+ // Ensure selection does not occur
+ endX = origX + mTouchSlop + 5;
+ endY = origY + magnitudeY;
+ } else if (e2.getY() < e1.getY()) {
+ if (DEBUG) Slog.d(TAG, "Detected up motion");
+ endX = origX + mTouchSlop + 5;
+ endY = origY - magnitudeY;
+ } else if (e2.getX() > e1.getX()) {
+ if (DEBUG) Slog.d(TAG, "Detected left motion");
+ endX = origX + magnitudeX;
+ endY = origY + mTouchSlop + 5;
+ } else if (e2.getX() < e1.getX()) {
+ if (DEBUG) Slog.d(TAG, "Detected right motion");
+ endX = origX - magnitudeX;
+ endY = origY + mTouchSlop + 5;
+ } else {
+ return false;
+ }
+
+ sendSwipe(origX, origY, endX, endY);
+ return true;
+ }
+
+ private void sendSwipe(float x1, float y1, float x2, float y2) {
+ final long duration = 100;
+ long now = SystemClock.uptimeMillis();
+ final long startTime = now;
+ final long endTime = startTime + duration;
+ sendMotionEvent(MotionEvent.ACTION_DOWN, now, x1, y1, 1.0f);
+
+ while (now < endTime) {
+ long elapsedTime = now - startTime;
+ float alpha = (float) elapsedTime / duration;
+ sendMotionEvent(MotionEvent.ACTION_MOVE, now,
+ lerp(x1, x2, alpha), lerp(y1, y2, alpha), 1.0f);
+ now = SystemClock.uptimeMillis();
+ }
+ sendMotionEvent(MotionEvent.ACTION_UP, now, x2, y2, 1.0f);
+ }
+
+ private void sendMotionEvent(int action, long when, float x, float y,
+ float pressure) {
+ final float DEFAULT_SIZE = 1.0f;
+ final int DEFAULT_META_STATE = 0;
+ final float DEFAULT_PRECISION_X = 1.0f;
+ final float DEFAULT_PRECISION_Y = 1.0f;
+ final int DEFAULT_DEVICE_ID = 0;
+ final int DEFAULT_EDGE_FLAGS = 0;
+
+ MotionEvent e = MotionEvent.obtain(when, when, action, x, y, pressure,
+ DEFAULT_SIZE, DEFAULT_META_STATE, DEFAULT_PRECISION_X,
+ DEFAULT_PRECISION_Y, DEFAULT_DEVICE_ID, DEFAULT_EDGE_FLAGS);
+ e.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ sendInputEvent(e);
+ }
+
+ private void sendInputEvent(InputEvent event) {
+ mInputManager.injectInputEvent(event,
+ InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
+ }
+
+ private static final float lerp(float a, float b, float alpha) {
+ return (b - a) * alpha + a;
+ }
+
+ @Override
+ public boolean onDown(MotionEvent e) {
+ return false;
+ }
+
+ @Override
+ public void onShowPress(MotionEvent e) {
+ }
+
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ return false;
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
+ float distanceY) {
+ return false;
+ }
+
+ @Override
+ public void onLongPress(MotionEvent e) {
+ if (mLongPressPendingIntent != null) {
+ try {
+ mLongPressPendingIntent.send();
+ } catch (CanceledException e1) {
+ e1.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+ float velocityY) {
+ return generateSwipe(e1, e2);
+ }
+
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent e) {
+ return false;
+ }
+
+ @Override
+ public boolean onDoubleTap(MotionEvent e) {
+ if (mDoubleClickPendingIntent != null) {
+ try {
+ mDoubleClickPendingIntent.send();
+ return true;
+ } catch (CanceledException e1) {
+ e1.printStackTrace();
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean onDoubleTapEvent(MotionEvent e) {
+ return false;
+ }
+
+ public void setOnLongPressPendingIntent(PendingIntent pendingIntent) {
+ mLongPressPendingIntent = pendingIntent;
+ }
+
+ public void setOnDoubleClickPendingIntent(PendingIntent pendingIntent) {
+ mDoubleClickPendingIntent = pendingIntent;
+ }
+}
diff --git a/services/core/java/com/android/server/gesture/GestureService.java b/services/core/java/com/android/server/gesture/GestureService.java
new file mode 100644
index 0000000..1a01e41
--- /dev/null
+++ b/services/core/java/com/android/server/gesture/GestureService.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod Project (Jens Doll)
+ *
+ * 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 com.android.server.gesture;
+
+import android.Manifest;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.service.gesture.IGestureService;
+import android.util.Slog;
+
+import com.android.server.input.InputManagerService;
+
+/**
+ * A system service to track gesture sensor gestures. This service is
+ * responsible for creating input events from motion events generated by
+ * gesture sensor input hardware:
+ * <li>Installing an input filter to listen for gesture sensor events</li>
+ * <li>Generating input events to be injected into the input stream</li>
+ */
+public class GestureService extends IGestureService.Stub {
+ public static final String TAG = "GestureService";
+ public static final boolean DEBUG = false;
+
+ private Context mContext;
+ private InputManagerService mInputManager;
+ private GestureInputFilter mInputFilter;
+
+ public GestureService(Context context, InputManagerService inputManager) {
+ mContext = context;
+ mInputManager = inputManager;
+ }
+
+ // called by system server
+ public void systemReady() {
+ if (DEBUG) Slog.d(TAG, "Starting Gesture Sensor service");
+ mInputFilter = new GestureInputFilter(mContext);
+ mInputManager.registerSecondaryInputFilter(mInputFilter);
+ }
+
+ public void setOnLongPressPendingIntent(PendingIntent pendingIntent) {
+ mInputFilter.setOnLongPressPendingIntent(pendingIntent);
+ }
+
+ public void setOnDoubleClickPendingIntent(PendingIntent pendingIntent) {
+ mInputFilter.setOnDoubleClickPendingIntent(pendingIntent);
+ }
+}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 17b4f9c..1919671 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -150,8 +150,9 @@ public class InputManagerService extends IInputManager.Stub
// State for the currently installed input filter.
final Object mInputFilterLock = new Object();
- IInputFilter mInputFilter; // guarded by mInputFilterLock
- InputFilterHost mInputFilterHost; // guarded by mInputFilterLock
+ ChainedInputFilterHost mInputFilterHost; // guarded by mInputFilterLock
+ ArrayList<ChainedInputFilterHost> mInputFilterChain =
+ new ArrayList<ChainedInputFilterHost>(); // guarded by mInputFilterLock
private static native long nativeInit(InputManagerService service,
Context context, MessageQueue messageQueue);
@@ -186,6 +187,8 @@ public class InputManagerService extends IInputManager.Stub
InputChannel fromChannel, InputChannel toChannel);
private static native void nativeSetPointerSpeed(long ptr, int speed);
private static native void nativeSetShowTouches(long ptr, boolean enabled);
+ private static native void nativeSetStylusIconEnabled(long ptr, boolean enabled);
+ private static native void nativeSetVolumeKeysRotation(long ptr, int mode);
private static native void nativeSetInteractive(long ptr, boolean interactive);
private static native void nativeReloadCalibration(long ptr);
private static native void nativeVibrate(long ptr, int deviceId, long[] pattern,
@@ -288,17 +291,22 @@ public class InputManagerService extends IInputManager.Stub
registerPointerSpeedSettingObserver();
registerShowTouchesSettingObserver();
+ registerStylusIconEnabledSettingObserver();
+ registerVolumeKeysRotationSettingObserver();
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
+ updateVolumeKeysRotationFromSettings();
}
}, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
+ updateStylusIconEnabledFromSettings();
+ updateVolumeKeysRotationFromSettings();
}
// TODO(BT) Pass in paramter for bluetooth system
@@ -495,34 +503,71 @@ public class InputManagerService extends IInputManager.Stub
*/
public void setInputFilter(IInputFilter filter) {
synchronized (mInputFilterLock) {
- final IInputFilter oldFilter = mInputFilter;
- if (oldFilter == filter) {
- return; // nothing to do
- }
-
- if (oldFilter != null) {
- mInputFilter = null;
+ if (mInputFilterHost != null) {
mInputFilterHost.disconnectLocked();
+ mInputFilterChain.remove(mInputFilterHost);
mInputFilterHost = null;
- try {
- oldFilter.uninstall();
- } catch (RemoteException re) {
- /* ignore */
- }
}
if (filter != null) {
- mInputFilter = filter;
- mInputFilterHost = new InputFilterHost();
- try {
- filter.install(mInputFilterHost);
- } catch (RemoteException re) {
- /* ignore */
+ ChainedInputFilterHost head = mInputFilterChain.isEmpty() ? null :
+ mInputFilterChain.get(0);
+ mInputFilterHost = new ChainedInputFilterHost(filter, head);
+ mInputFilterHost.connectLocked();
+ mInputFilterChain.add(0, mInputFilterHost);
+ }
+
+ nativeSetInputFilterEnabled(mPtr, !mInputFilterChain.isEmpty());
+ }
+ }
+
+ /**
+ * Registers a secondary input filter. These filters are always behind the "original"
+ * input filter. This ensures that all input events will be filtered by the
+ * {@code AccessibilityManagerService} first.
+ * <p>
+ * <b>Note:</b> Even though this implementation using AIDL interfaces, it is designed to only
+ * provide direct access. Therefore, any filter registering should reside in the
+ * system server DVM only!
+ *
+ * @param filter The input filter to register.
+ */
+ public void registerSecondaryInputFilter(IInputFilter filter) {
+ synchronized (mInputFilterLock) {
+ ChainedInputFilterHost host = new ChainedInputFilterHost(filter, null);
+ if (!mInputFilterChain.isEmpty()) {
+ mInputFilterChain.get(mInputFilterChain.size() - 1).mNext = host;
+ }
+ host.connectLocked();
+ mInputFilterChain.add(host);
+
+ nativeSetInputFilterEnabled(mPtr, !mInputFilterChain.isEmpty());
+ }
+ }
+
+ public void unregisterSecondaryInputFilter(IInputFilter filter) {
+ synchronized (mInputFilterLock) {
+ int index = findInputFilterIndexLocked(filter);
+ if (index >= 0) {
+ ChainedInputFilterHost host = mInputFilterChain.get(index);
+ host.disconnectLocked();
+ if (index >= 1) {
+ mInputFilterChain.get(index - 1).mNext = host.mNext;
}
+ mInputFilterChain.remove(index);
}
- nativeSetInputFilterEnabled(mPtr, filter != null);
+ nativeSetInputFilterEnabled(mPtr, !mInputFilterChain.isEmpty());
+ }
+ }
+
+ private int findInputFilterIndexLocked(IInputFilter filter) {
+ for (int i = 0; i < mInputFilterChain.size(); i++) {
+ if (mInputFilterChain.get(i).mInputFilter == filter) {
+ return i;
+ }
}
+ return -1;
}
@Override // Binder call
@@ -1280,6 +1325,58 @@ public class InputManagerService extends IInputManager.Stub
return result;
}
+ public void updateStylusIconEnabledFromSettings() {
+ int enabled = getStylusIconEnabled(0);
+ nativeSetStylusIconEnabled(mPtr, enabled != 0);
+ }
+
+ public void registerStylusIconEnabledSettingObserver() {
+ mContext.getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(Settings.System.STYLUS_ICON_ENABLED), false,
+ new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateStylusIconEnabledFromSettings();
+ }
+ });
+ }
+
+ private int getStylusIconEnabled(int defaultValue) {
+ int result = defaultValue;
+ try {
+ result = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.STYLUS_ICON_ENABLED);
+ } catch (SettingNotFoundException snfe) {
+ }
+ return result;
+ }
+
+ public void updateVolumeKeysRotationFromSettings() {
+ int mode = getVolumeKeysRotationSetting(0);
+ nativeSetVolumeKeysRotation(mPtr, mode);
+ }
+
+ public void registerVolumeKeysRotationSettingObserver() {
+ mContext.getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(Settings.System.SWAP_VOLUME_KEYS_ON_ROTATION), false,
+ new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateVolumeKeysRotationFromSettings();
+ }
+ });
+ }
+
+ private int getVolumeKeysRotationSetting(int defaultValue) {
+ int result = defaultValue;
+ try {
+ result = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.SWAP_VOLUME_KEYS_ON_ROTATION, UserHandle.USER_CURRENT);
+ } catch (SettingNotFoundException snfe) {
+ }
+ return result;
+ }
+
// Binder call
@Override
public void vibrate(int deviceId, long[] pattern, int repeat, IBinder token) {
@@ -1435,15 +1532,22 @@ public class InputManagerService extends IInputManager.Stub
// Native callback.
final boolean filterInputEvent(InputEvent event, int policyFlags) {
+ ChainedInputFilterHost head = null;
synchronized (mInputFilterLock) {
- if (mInputFilter != null) {
- try {
- mInputFilter.filterInputEvent(event, policyFlags);
- } catch (RemoteException e) {
- /* ignore */
- }
- return false;
+ if (!mInputFilterChain.isEmpty()) {
+ head = mInputFilterChain.get(0);
+ }
+ }
+ // call filter input event outside of the lock.
+ // this is safe, because we know that mInputFilter never changes.
+ // we may loose a event, but this does not differ from the original implementation.
+ if (head != null) {
+ try {
+ head.mInputFilter.filterInputEvent(event, policyFlags);
+ } catch (RemoteException e) {
+ /* ignore */
}
+ return false;
}
event.recycle();
return true;
@@ -1694,6 +1798,66 @@ public class InputManagerService extends IInputManager.Stub
}
}
+ /**
+ * Hosting interface for input filters to call back into the input manager.
+ */
+ private final class ChainedInputFilterHost extends IInputFilterHost.Stub {
+ private final IInputFilter mInputFilter;
+ private ChainedInputFilterHost mNext;
+ private boolean mDisconnected;
+
+ private ChainedInputFilterHost(IInputFilter filter, ChainedInputFilterHost next) {
+ mInputFilter = filter;
+ mNext = next;
+ mDisconnected = false;
+ }
+
+ public void connectLocked() {
+ try {
+ mInputFilter.install(this);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+
+ public void disconnectLocked() {
+ try {
+ mInputFilter.uninstall();
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ // DO NOT set mInputFilter to null here! mInputFilter is used outside of the lock!
+ mDisconnected = true;
+ }
+
+ @Override
+ public void sendInputEvent(InputEvent event, int policyFlags) {
+ if (event == null) {
+ throw new IllegalArgumentException("event must not be null");
+ }
+
+ synchronized (mInputFilterLock) {
+ if (!mDisconnected) {
+ if (mNext == null) {
+ nativeInjectInputEvent(mPtr, event, Display.DEFAULT_DISPLAY, 0, 0,
+ InputManager.INJECT_INPUT_EVENT_MODE_ASYNC, 0,
+ policyFlags | WindowManagerPolicy.FLAG_FILTERED);
+ } else {
+ try {
+ // We need to pass a copy into filterInputEvent as it assumes
+ // the callee takes responsibility and recycles it - in case
+ // multiple filters are chained, calling into the second filter
+ // will cause event to be recycled twice
+ mNext.mInputFilter.filterInputEvent(event.copy(), policyFlags);
+ } catch (RemoteException e) {
+ /* ignore */
+ }
+ }
+ }
+ }
+ }
+ }
+
private static final class KeyboardLayoutDescriptor {
public String packageName;
public String receiverName;
diff --git a/services/core/java/com/android/server/lights/Light.java b/services/core/java/com/android/server/lights/Light.java
index b496b4c..3eb570c 100644
--- a/services/core/java/com/android/server/lights/Light.java
+++ b/services/core/java/com/android/server/lights/Light.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2013 The Android Open Source Project
+ * 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.
@@ -35,6 +36,7 @@ public abstract class Light {
public abstract void setBrightness(int brightness, int brightnessMode);
public abstract void setColor(int color);
public abstract void setFlashing(int color, int mode, int onMS, int offMS);
+ public abstract void setModes(int brightnessLevel, boolean multipleLeds);
public abstract void pulse();
public abstract void pulse(int color, int onMS);
public abstract void turnOff();
diff --git a/services/core/java/com/android/server/lights/LightsManager.java b/services/core/java/com/android/server/lights/LightsManager.java
index 2f20509..e1e5aa3 100644
--- a/services/core/java/com/android/server/lights/LightsManager.java
+++ b/services/core/java/com/android/server/lights/LightsManager.java
@@ -25,7 +25,9 @@ public abstract class LightsManager {
public static final int LIGHT_ID_ATTENTION = 5;
public static final int LIGHT_ID_BLUETOOTH = 6;
public static final int LIGHT_ID_WIFI = 7;
- public static final int LIGHT_ID_COUNT = 8;
+ public static final int LIGHT_ID_CAPS = 8;
+ public static final int LIGHT_ID_FUNC = 9;
+ public static final int LIGHT_ID_COUNT = 10;
public abstract Light getLight(int id);
}
diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java
index ed884ef..16dde26 100644
--- a/services/core/java/com/android/server/lights/LightsService.java
+++ b/services/core/java/com/android/server/lights/LightsService.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -34,6 +35,9 @@ public class LightsService extends SystemService {
private LightImpl(int id) {
mId = id;
+ mBrightnessLevel = 0xFF;
+ mModesUpdate = false;
+ mMultipleLeds = false;
}
@Override
@@ -65,6 +69,20 @@ public class LightsService extends SystemService {
}
@Override
+ public void setModes(int brightnessLevel, boolean multipleLeds) {
+ synchronized (this) {
+ if (mBrightnessLevel != brightnessLevel) {
+ mBrightnessLevel = brightnessLevel;
+ mModesUpdate = true;
+ }
+ if (mMultipleLeds != multipleLeds) {
+ mMultipleLeds = multipleLeds;
+ mModesUpdate = true;
+ }
+ }
+ }
+
+ @Override
public void pulse() {
pulse(0x00ffffff, 7);
}
@@ -94,17 +112,20 @@ public class LightsService extends SystemService {
}
private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) {
- if (color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS) {
+ if (mModesUpdate || color != mColor || mode != mMode || onMS != mOnMS ||
+ offMS != mOffMS) {
if (DEBUG) Slog.v(TAG, "setLight #" + mId + ": color=#"
+ Integer.toHexString(color));
mColor = color;
mMode = mode;
mOnMS = onMS;
mOffMS = offMS;
+ mModesUpdate = false;
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLight(" + mId + ", 0x"
+ Integer.toHexString(color) + ")");
try {
- setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode);
+ setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode,
+ mBrightnessLevel, mMultipleLeds ? 1 : 0);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
@@ -116,7 +137,10 @@ public class LightsService extends SystemService {
private int mMode;
private int mOnMS;
private int mOffMS;
+ private int mBrightnessLevel;
private boolean mFlashing;
+ private boolean mModesUpdate;
+ private boolean mMultipleLeds;
}
public LightsService(Context context) {
@@ -163,7 +187,8 @@ public class LightsService extends SystemService {
private static native void finalize_native(long ptr);
static native void setLight_native(long ptr, int light, int color, int mode,
- int onMS, int offMS, int brightnessMode);
+ int onMS, int offMS, int brightnessMode, int brightnessLevel,
+ int mMultipleLeds);
private long mNativePointer;
}
diff --git a/services/core/java/com/android/server/location/GeoFencerBase.java b/services/core/java/com/android/server/location/GeoFencerBase.java
new file mode 100644
index 0000000..eec07ab
--- /dev/null
+++ b/services/core/java/com/android/server/location/GeoFencerBase.java
@@ -0,0 +1,147 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * Not a Contribution, Apache license notifications and license are retained
+ * for attribution purposes only.
+ *
+ * Copyright (C) 2007 The Android Open Source 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 com.android.server.location;
+
+import android.os.Binder;
+import android.os.Parcelable;
+import android.util.Log;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.location.GeoFenceParams;
+import android.location.ILocationListener;
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Collection;
+import java.util.ArrayList;
+
+/**
+ * This class defines a base class for GeoFencers
+ *
+ * @hide
+ */
+public abstract class GeoFencerBase {
+ private static final String TAG = "GeoFencerBase";
+ private HashMap<PendingIntent,GeoFenceParams> mGeoFences;
+
+ public GeoFencerBase() {
+ mGeoFences = new HashMap<PendingIntent,GeoFenceParams>();
+ }
+
+ public void add(double latitude, double longitude,
+ float radius, long expiration, PendingIntent intent,
+ String packageName) {
+ add(new GeoFenceParams(latitude, longitude, radius,
+ expiration, intent, packageName));
+ }
+
+ public void add(GeoFenceParams geoFence) {
+ synchronized(mGeoFences) {
+ mGeoFences.put(geoFence.mIntent, geoFence);
+ }
+ if (!start(geoFence)) {
+ synchronized(mGeoFences) {
+ mGeoFences.remove(geoFence.mIntent);
+ }
+ }
+ }
+
+ public void remove(PendingIntent intent) {
+ remove(intent, false);
+ }
+
+ public void remove(PendingIntent intent, boolean localOnly) {
+ GeoFenceParams geoFence = null;
+
+ synchronized(mGeoFences) {
+ geoFence = mGeoFences.remove(intent);
+ }
+
+ if (geoFence != null) {
+ if (!localOnly && !stop(intent)) {
+ synchronized(mGeoFences) {
+ mGeoFences.put(geoFence.mIntent, geoFence);
+ }
+ }
+ }
+ }
+
+ public int getNumbOfGeoFences() {
+ return mGeoFences.size();
+ }
+
+ public Collection<GeoFenceParams> getAllGeoFences() {
+ return mGeoFences.values();
+ }
+
+ public GeoFenceParams getGeoFence(PendingIntent intent) {
+ return mGeoFences.get(intent);
+ }
+
+ public boolean hasCaller(int uid) {
+ for (GeoFenceParams alert : mGeoFences.values()) {
+ if (alert.mUid == uid) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void removeCaller(int uid) {
+ ArrayList<PendingIntent> removedFences = null;
+ for (GeoFenceParams alert : mGeoFences.values()) {
+ if (alert.mUid == uid) {
+ if (removedFences == null) {
+ removedFences = new ArrayList<PendingIntent>();
+ }
+ removedFences.add(alert.mIntent);
+ }
+ }
+ if (removedFences != null) {
+ for (int i = removedFences.size()-1; i>=0; i--) {
+ mGeoFences.remove(removedFences.get(i));
+ }
+ }
+ }
+
+ public void transferService(GeoFencerBase geofencer) {
+ for (GeoFenceParams alert : geofencer.mGeoFences.values()) {
+ geofencer.stop(alert.mIntent);
+ add(alert);
+ }
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ if (mGeoFences.size() > 0) {
+ pw.println(prefix + " GeoFences:");
+ prefix += " ";
+ for (Map.Entry<PendingIntent, GeoFenceParams> i
+ : mGeoFences.entrySet()) {
+ pw.println(prefix + i.getKey() + ":");
+ i.getValue().dump(pw, prefix);
+ }
+ }
+ }
+
+ abstract protected boolean start(GeoFenceParams geoFence);
+ abstract protected boolean stop(PendingIntent intent);
+}
diff --git a/services/core/java/com/android/server/location/GeoFencerProxy.java b/services/core/java/com/android/server/location/GeoFencerProxy.java
new file mode 100644
index 0000000..8ffbe8c
--- /dev/null
+++ b/services/core/java/com/android/server/location/GeoFencerProxy.java
@@ -0,0 +1,149 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * Not a Contribution, Apache license notifications and license are retained
+ * for attribution purposes only.
+ *
+ * Copyright (C) 2007 The Android Open Source 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 com.android.server.location;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.app.PendingIntent;
+import android.location.IGeoFencer;
+import android.location.IGeoFenceListener;
+import android.location.GeoFenceParams;
+
+/**
+ * A class for proxying IGeoFenceProvider implementations.
+ *
+ * {@hide}
+ */
+public class GeoFencerProxy extends GeoFencerBase {
+
+ private static final String TAG = "GeoFencerProxy";
+ private static final boolean LOGV_ENABLED = true;
+
+ private final Context mContext;
+ private final Intent mIntent;
+ private IGeoFencer mGeoFencer;
+
+ private final ServiceConnection mServiceConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ synchronized (this) {
+ mGeoFencer = IGeoFencer.Stub.asInterface(service);
+ notifyAll();
+ }
+ Log.v(TAG, "onServiceConnected: mGeoFencer - "+mGeoFencer);
+ }
+ public void onServiceDisconnected(ComponentName className) {
+ synchronized (this) {
+ mGeoFencer = null;
+ }
+ Log.v(TAG, "onServiceDisconnected");
+ }
+ };
+
+ private final IGeoFenceListener.Stub mListener = new IGeoFenceListener.Stub() {
+ @Override
+ public void geoFenceExpired(PendingIntent intent) throws RemoteException {
+ logv("geoFenceExpired - "+intent);
+ remove(intent, true);
+ }
+ };
+
+ private static GeoFencerProxy mGeoFencerProxy;
+ public static GeoFencerProxy getGeoFencerProxy(Context context, String serviceName) {
+ if (mGeoFencerProxy == null) {
+ mGeoFencerProxy = new GeoFencerProxy(context, serviceName);
+ }
+ return mGeoFencerProxy;
+ }
+
+ private GeoFencerProxy(Context context, String serviceName) {
+ mContext = context;
+ mIntent = new Intent(IGeoFencer.class.getName());
+ mIntent.setPackage(serviceName);
+ mContext.bindService(mIntent, mServiceConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
+ | Context.BIND_ALLOW_OOM_MANAGEMENT);
+ }
+
+ public void removeCaller(int uid) {
+ super.removeCaller(uid);
+ if(mGeoFencer != null) {
+ try {
+ mGeoFencer.clearGeoFenceUser(uid);
+ } catch (RemoteException re) {
+ }
+ }
+ else
+ Log.e(TAG, "removeCaller - mGeoFencer is null");
+ }
+
+ private boolean ensureGeoFencer() {
+ if (mGeoFencer == null) {
+ try {
+ synchronized(mServiceConnection) {
+ logv("waiting...");
+ mServiceConnection.wait(60000);
+ logv("woke up!!!");
+ }
+ } catch (InterruptedException ie) {
+ Log.w(TAG, "Interrupted while waiting for GeoFencer");
+ return false;
+ }
+
+ if (mGeoFencer == null) {
+ Log.w(TAG, "Timed out. No GeoFencer connection");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected boolean start(GeoFenceParams geofence) {
+ if (ensureGeoFencer()) {
+ try {
+ return mGeoFencer.setGeoFence(mListener, geofence);
+ } catch (RemoteException re) {
+ }
+ }
+ return false;
+ }
+
+ protected boolean stop(PendingIntent intent) {
+ if (ensureGeoFencer()) {
+ try {
+ mGeoFencer.clearGeoFence(mListener, intent);
+ return true;
+ } catch (RemoteException re) {
+ }
+ }
+ return false;
+ }
+
+ private void logv(String s) {
+ if (LOGV_ENABLED) Log.v(TAG, s);
+ }
+}
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index ba5f516..13db771 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -1929,7 +1929,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
type = AGPS_REF_LOCATION_TYPE_GSM_CELLID;
}
native_agps_set_ref_location_cellid(type, mcc, mnc,
- gsm_cell.getLac(), gsm_cell.getCid());
+ gsm_cell.getLac(), gsm_cell.getCid(), gsm_cell.getPsc());
} else {
Log.e(TAG,"Error getting cell location info.");
}
@@ -2287,7 +2287,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
// AGPS ril suport
private native void native_agps_set_ref_location_cellid(int type, int mcc, int mnc,
- int lac, int cid);
+ int lac, int cid, int psc);
private native void native_agps_set_id(int type, String setid);
private native void native_update_network_state(boolean connected, int type,
diff --git a/services/core/java/com/android/server/net/NetPluginDelegate.java b/services/core/java/com/android/server/net/NetPluginDelegate.java
index 192905a..6716a6b 100644
--- a/services/core/java/com/android/server/net/NetPluginDelegate.java
+++ b/services/core/java/com/android/server/net/NetPluginDelegate.java
@@ -45,9 +45,13 @@ class NetPluginDelegate {
private static Class tetherExtensionClass = null;
private static Object tetherExtensionObj = null;
+ private static boolean extensionFailed;
+
static void getTetherStats(NetworkStats uidStats, NetworkStats devStats,
NetworkStats xtStats) {
- loadTetherExtJar();
+ if (!loadTetherExtJar()) {
+ return;
+ }
try {
tetherExtensionClass.getMethod("getTetherStats", NetworkStats.class,
NetworkStats.class, NetworkStats.class).invoke(tetherExtensionObj, uidStats,
@@ -59,7 +63,9 @@ class NetPluginDelegate {
}
static void setQuota(String iface, long quota) {
- loadTetherExtJar();
+ if (!loadTetherExtJar()) {
+ return;
+ }
try {
tetherExtensionClass.getMethod("setQuota", String.class, long.class).invoke(
tetherExtensionObj, iface, quota);
@@ -70,24 +76,26 @@ class NetPluginDelegate {
- private static void loadTetherExtJar() {
+ private static boolean loadTetherExtJar() {
final String realProvider = "com.qualcomm.qti.tetherstatsextension.TetherStatsReporting";
final String realProviderPath = "/system/framework/ConnectivityExt.jar";
- if (tetherExtensionClass == null && tetherExtensionObj == null) {
- if (LOGV) Slog.v(TAG, "loading ConnectivityExt jar");
- try {
+ if (!extensionFailed && tetherExtensionClass == null && tetherExtensionObj == null) {
+ if (LOGV) Slog.v(TAG, "loading ConnectivityExt jar");
+ try {
- PathClassLoader classLoader = new PathClassLoader(realProviderPath,
- ClassLoader.getSystemClassLoader());
+ PathClassLoader classLoader = new PathClassLoader(realProviderPath,
+ ClassLoader.getSystemClassLoader());
- tetherExtensionClass = classLoader.loadClass(realProvider);
- tetherExtensionObj = tetherExtensionClass.newInstance();
- if (LOGV)
- Slog.v(TAG, "ConnectivityExt jar loaded");
- } catch (Exception e) {
- e.printStackTrace();
- Log.w(TAG, "unable to ConnectivityExt jar");
- }
+ tetherExtensionClass = classLoader.loadClass(realProvider);
+ tetherExtensionObj = tetherExtensionClass.newInstance();
+ if (LOGV)
+ Slog.v(TAG, "ConnectivityExt jar loaded");
+ extensionFailed = false;
+ } catch (Exception e) {
+ Log.w(TAG, "Connectivity extension is not available");
+ extensionFailed = true;
+ }
}
+ return !extensionFailed;
}
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 522ec48..f041f0d 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -804,7 +804,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
} else {
notifyUnderLimitLocked(policy.template);
- if (policy.isOverWarning(totalBytes) && policy.lastWarningSnooze < start) {
+ if (policy.isOverWarning(totalBytes) && policy.lastWarningSnooze < start
+ && policy.limitBytes != LIMIT_DISABLED) {
enqueueNotification(policy, TYPE_WARNING, totalBytes);
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e6fcdf1..f549f7c 100644..100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2007 The Android Open Source Project
+ * 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.
@@ -23,6 +24,7 @@ import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
@@ -30,6 +32,7 @@ import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.ITransientNotification;
+import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
@@ -41,7 +44,9 @@ import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
+import android.content.ContentValues;
import android.content.Context;
+import android.database.Cursor;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
@@ -58,6 +63,9 @@ import android.media.AudioManager;
import android.media.AudioManagerInternal;
import android.media.AudioSystem;
import android.media.IRingtonePlayer;
+import android.media.session.MediaController;
+import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -92,7 +100,9 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
+import android.util.LruCache;
import android.util.Slog;
+import android.util.SparseIntArray;
import android.util.Xml;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -101,6 +111,9 @@ import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.cm.SpamFilter;
+import com.android.internal.util.cm.SpamFilter.SpamContract.NotificationTable;
+import com.android.internal.util.cm.SpamFilter.SpamContract.PackageTable;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -134,10 +147,15 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
/** {@hide} */
public class NotificationManagerService extends SystemService {
@@ -170,6 +188,8 @@ public class NotificationManagerService extends SystemService {
static final int JUNK_SCORE = -1000;
static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
+ private static final String IS_FILTERED_QUERY = NotificationTable.NORMALIZED_TEXT + "=? AND " +
+ PackageTable.PACKAGE_NAME + "=?";
// Notifications with scores below this will not interrupt the user, either via LED or
// sound or vibration
@@ -198,6 +218,9 @@ public class NotificationManagerService extends SystemService {
/** notification_enqueue status value for an ignored notification. */
private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
+ /** notification light maximum brightness value to use. */
+ private static final int LIGHT_BRIGHTNESS_MAXIMUM = 255;
+
private IActivityManager mAm;
AudioManager mAudioManager;
AudioManagerInternal mAudioManagerInternal;
@@ -217,10 +240,35 @@ public class NotificationManagerService extends SystemService {
private int mDefaultNotificationLedOff;
private long[] mDefaultVibrationPattern;
+ private boolean mAdjustableNotificationLedBrightness;
+ private int mNotificationLedBrightnessLevel = LIGHT_BRIGHTNESS_MAXIMUM;
+
+ private boolean mMultipleNotificationLeds;
+ private boolean mMultipleLedsEnabledSetting = false;
+
+ private boolean mScreenOnEnabled = false;
+ private boolean mScreenOnDefault = false;
+
private long[] mFallbackVibrationPattern;
private boolean mUseAttentionLight;
boolean mSystemReady;
+ private final SparseIntArray mSpamCache;
+ private ExecutorService mSpamExecutor = Executors.newSingleThreadExecutor();
+
+ private static final Uri FILTER_MSG_URI = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(SpamFilter.AUTHORITY)
+ .appendPath("messages")
+ .build();
+
+ private static final Uri UPDATE_MSG_URI = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(SpamFilter.AUTHORITY)
+ .appendPath(SpamFilter.MESSAGE_PATH)
+ .appendEncodedPath("inc_count")
+ .build();
+
private boolean mDisableNotificationEffects;
private int mCallState;
private String mSoundNotificationKey;
@@ -235,6 +283,11 @@ public class NotificationManagerService extends SystemService {
private boolean mScreenOn = true;
private boolean mInCall = false;
private boolean mNotificationPulseEnabled;
+ private HashMap<String, NotificationLedValues> mNotificationPulseCustomLedValues;
+ private Map<String, String> mPackageNameMappings;
+
+ // for checking lockscreen status
+ private KeyguardManager mKeyguardManager;
// used as a mutex for access to all active notifications & listeners
final ArrayList<NotificationRecord> mNotificationList =
@@ -275,6 +328,8 @@ public class NotificationManagerService extends SystemService {
private NotificationListeners mListeners;
private ConditionProviders mConditionProviders;
private NotificationUsageStats mUsageStats;
+ private boolean mDisableDuckingWhileMedia;
+ private boolean mActiveMedia;
private static final int MY_UID = Process.myUid();
private static final int MY_PID = Process.myPid();
@@ -487,6 +542,12 @@ public class NotificationManagerService extends SystemService {
}
}
+ class NotificationLedValues {
+ public int color;
+ public int onMS;
+ public int offMS;
+ }
+
private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
@Override
@@ -610,9 +671,12 @@ public class NotificationManagerService extends SystemService {
Binder.restoreCallingIdentity(identity);
}
- // light
- mLights.clear();
- updateLightsLocked();
+ // lights
+ // clear only if lockscreen is not active
+ if (mKeyguardManager != null && !mKeyguardManager.isKeyguardLocked()) {
+ mLights.clear();
+ updateLightsLocked();
+ }
}
}
@@ -774,12 +838,16 @@ public class NotificationManagerService extends SystemService {
}
} else if (action.equals(Intent.ACTION_USER_PRESENT)) {
// turn off LED when user passes through lock screen
- mNotificationLight.turnOff();
- mStatusBar.notificationLightOff();
+ // if lights with screen on is disabled.
+ if (!mScreenOnEnabled) {
+ mNotificationLight.turnOff();
+ mStatusBar.notificationLightOff();
+ }
} else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
// reload per-user settings
mSettingsObserver.update(null);
+ mSpamFilterObserver.update(null);
mUserProfiles.updateCache(context);
// Refresh managed services
mConditionProviders.onUserSwitched(user);
@@ -794,9 +862,103 @@ public class NotificationManagerService extends SystemService {
}
};
- private final class SettingsObserver extends ContentObserver {
+ class SpamFilterObserver extends ContentObserver {
+
+ private Future mTask;
+
+ public SpamFilterObserver(Handler handler) {
+ super(handler);
+ }
+
+ private void addToCache(Cursor c) {
+ int notifId = c.getInt(c.getColumnIndex(
+ NotificationTable.ID));
+ String pkgName = c.getString(c.getColumnIndex(
+ PackageTable.PACKAGE_NAME));
+ String normalizedText = c.getString(c.getColumnIndex(
+ NotificationTable.NORMALIZED_TEXT));
+ int hash = getSpamCacheHash(normalizedText, pkgName);
+ synchronized (mSpamCache) {
+ mSpamCache.put(hash, notifId);
+ }
+ }
+
+ private Runnable mFetchAllFilters = new Runnable() {
+ @Override
+ public void run() {
+ Cursor c = getContext().getContentResolver().query(FILTER_MSG_URI,
+ null, null, null, null);
+ if (c != null) {
+ synchronized (mSpamCache) {
+ mSpamCache.clear();
+ while (c.moveToNext()) {
+ addToCache(c);
+ if (Thread.interrupted()) {
+ break;
+ }
+ c.close();
+ }
+ }
+ }
+ }
+ };
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ update(uri);
+ }
+
+ void update(final Uri uri) {
+ if (mTask != null && !mTask.isDone()) {
+ mTask.cancel(true);
+ }
+ if (uri == null) {
+ mTask = mSpamExecutor.submit(mFetchAllFilters);
+ } else {
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ String id = uri.getLastPathSegment();
+ Cursor c = getContext().getContentResolver().query(
+ uri, null, null, null, null);
+
+ if (c != null) {
+ int index;
+ synchronized (mSpamCache) {
+ index = mSpamCache.indexOfValue(Integer.parseInt(id));
+ }
+ if (!c.moveToFirst()) {
+ synchronized (mSpamCache) {
+ // Filter was deleted
+ if (index >= 0) {
+ mSpamCache.removeAt(index);
+ }
+ }
+ } else if (index < 0) {
+ // Filter was added/updated
+ addToCache(c);
+ }
+ c.close();
+ }
+ }
+ };
+ mTask = mSpamExecutor.submit(r);
+ }
+ }
+
+ public void observe() {
+ ContentResolver resolver = getContext().getContentResolver();
+ resolver.registerContentObserver(SpamFilter.NOTIFICATION_URI,
+ true, this, UserHandle.USER_ALL);
+ update(null);
+ }
+ }
+
+ class SettingsObserver extends ContentObserver {
private final Uri NOTIFICATION_LIGHT_PULSE_URI
= Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
+ private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
+ = Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
SettingsObserver(Handler handler) {
super(handler);
@@ -804,8 +966,41 @@ public class NotificationManagerService extends SystemService {
void observe() {
ContentResolver resolver = getContext().getContentResolver();
- resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
+ resolver.registerContentObserver(
+ NOTIFICATION_LIGHT_PULSE_URI, false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(
+ ENABLED_NOTIFICATION_LISTENERS_URI, false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_COLOR),
false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_ON),
+ false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_OFF),
+ false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_ENABLE),
+ false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_VALUES),
+ false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.NOTIFICATION_LIGHT_SCREEN_ON),
+ false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.ZEN_DISABLE_DUCKING_DURING_MEDIA_PLAYBACK), false,
+ this, UserHandle.USER_ALL);
+ if (mAdjustableNotificationLedBrightness) {
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.NOTIFICATION_LIGHT_BRIGHTNESS_LEVEL),
+ false, this, UserHandle.USER_ALL);
+ }
+ if (mMultipleNotificationLeds) {
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.NOTIFICATION_LIGHT_MULTIPLE_LEDS_ENABLE),
+ false, this, UserHandle.USER_ALL);
+ }
update(null);
}
@@ -815,18 +1010,77 @@ public class NotificationManagerService extends SystemService {
public void update(Uri uri) {
ContentResolver resolver = getContext().getContentResolver();
- if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
- boolean pulseEnabled = Settings.System.getInt(resolver,
- Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
- if (mNotificationPulseEnabled != pulseEnabled) {
- mNotificationPulseEnabled = pulseEnabled;
- updateNotificationPulse();
- }
+
+ // LED enabled
+ mNotificationPulseEnabled = Settings.System.getIntForUser(resolver,
+ Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT) != 0;
+
+ // LED default color
+ mDefaultNotificationColor = Settings.System.getIntForUser(resolver,
+ Settings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_COLOR,
+ mDefaultNotificationColor, UserHandle.USER_CURRENT);
+
+ // LED default on MS
+ mDefaultNotificationLedOn = Settings.System.getIntForUser(resolver,
+ Settings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_ON,
+ mDefaultNotificationLedOn, UserHandle.USER_CURRENT);
+
+ // LED default off MS
+ mDefaultNotificationLedOff = Settings.System.getIntForUser(resolver,
+ Settings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_OFF,
+ mDefaultNotificationLedOff, UserHandle.USER_CURRENT);
+
+ // LED custom notification colors
+ mNotificationPulseCustomLedValues.clear();
+ if (Settings.System.getIntForUser(resolver,
+ Settings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_ENABLE, 0,
+ UserHandle.USER_CURRENT) != 0) {
+ parseNotificationPulseCustomValuesString(Settings.System.getStringForUser(resolver,
+ Settings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_VALUES,
+ UserHandle.USER_CURRENT));
+ }
+
+ // Notification LED brightness
+ if (mAdjustableNotificationLedBrightness) {
+ mNotificationLedBrightnessLevel = Settings.System.getIntForUser(resolver,
+ Settings.System.NOTIFICATION_LIGHT_BRIGHTNESS_LEVEL,
+ LIGHT_BRIGHTNESS_MAXIMUM, UserHandle.USER_CURRENT);
+ }
+
+ // Multiple LEDs enabled
+ if (mMultipleNotificationLeds) {
+ mMultipleLedsEnabledSetting = (Settings.System.getIntForUser(resolver,
+ Settings.System.NOTIFICATION_LIGHT_MULTIPLE_LEDS_ENABLE,
+ mMultipleNotificationLeds ? 1 : 0, UserHandle.USER_CURRENT) != 0);
}
+
+ // Notification lights with screen on
+ mScreenOnEnabled = (Settings.System.getIntForUser(resolver,
+ Settings.System.NOTIFICATION_LIGHT_SCREEN_ON,
+ mScreenOnDefault ? 1 : 0, UserHandle.USER_CURRENT) != 0);
+
+ updateNotificationPulse();
+
+ mDisableDuckingWhileMedia = Settings.Global.getInt(resolver,
+ Settings.Global.ZEN_DISABLE_DUCKING_DURING_MEDIA_PLAYBACK, 0) == 1;
+ updateDisableDucking();
+ }
+ }
+
+ private void updateDisableDucking() {
+ if (!mSystemReady) {
+ return;
+ }
+ final MediaSessionManager mediaSessionManager = (MediaSessionManager) getContext()
+ .getSystemService(Context.MEDIA_SESSION_SERVICE);
+ mediaSessionManager.removeOnActiveSessionsChangedListener(mSessionListener);
+ if (mDisableDuckingWhileMedia) {
+ mediaSessionManager.addOnActiveSessionsChangedListener(mSessionListener, null);
}
}
private SettingsObserver mSettingsObserver;
+ private SpamFilterObserver mSpamFilterObserver;
private ZenModeHelper mZenModeHelper;
private final Runnable mBuzzBeepBlinked = new Runnable() {
@@ -851,6 +1105,7 @@ public class NotificationManagerService extends SystemService {
public NotificationManagerService(Context context) {
super(context);
+ mSpamCache = new SparseIntArray();
}
@Override
@@ -861,6 +1116,8 @@ public class NotificationManagerService extends SystemService {
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
+ mKeyguardManager =
+ (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
mHandler = new WorkerHandler();
mRankingThread.start();
@@ -916,6 +1173,16 @@ public class NotificationManagerService extends SystemService {
mDefaultNotificationLedOff = resources.getInteger(
R.integer.config_defaultNotificationLedOff);
+ mNotificationPulseCustomLedValues = new HashMap<String, NotificationLedValues>();
+
+ mPackageNameMappings = new HashMap<String, String>();
+ final String[] defaultMapping = resources.getStringArray(
+ com.android.internal.R.array.notification_light_package_mapping);
+ for (String mapping : defaultMapping) {
+ String[] map = mapping.split("\\|");
+ mPackageNameMappings.put(map[0], map[1]);
+ }
+
mDefaultVibrationPattern = getLongArray(resources,
R.array.config_defaultNotificationVibePattern,
VIBRATE_PATTERN_MAXLEN,
@@ -926,6 +1193,11 @@ public class NotificationManagerService extends SystemService {
VIBRATE_PATTERN_MAXLEN,
DEFAULT_VIBRATE_PATTERN);
+ mAdjustableNotificationLedBrightness = resources.getBoolean(
+ com.android.internal.R.bool.config_adjustableNotificationLedBrightness);
+ mMultipleNotificationLeds = resources.getBoolean(
+ com.android.internal.R.bool.config_multipleNotificationLeds);
+
mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
// Don't start allowing notifications until the setup wizard has run once.
@@ -969,6 +1241,10 @@ public class NotificationManagerService extends SystemService {
null);
mSettingsObserver = new SettingsObserver(mHandler);
+ mSettingsObserver.observe();
+
+ mSpamFilterObserver = new SpamFilterObserver(mHandler);
+ mSpamFilterObserver.observe();
mArchive = new Archive(resources.getInteger(
R.integer.config_notificationServiceArchiveSize));
@@ -1011,10 +1287,13 @@ public class NotificationManagerService extends SystemService {
mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
mZenModeHelper.onSystemReady();
+
+ updateDisableDucking();
} else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
// This observer will force an update when observe is called, causing us to
// bind to listener services.
mSettingsObserver.observe();
+ mSpamFilterObserver.observe();
mListeners.onBootPhaseAppsCanStart();
mConditionProviders.onBootPhaseAppsCanStart();
}
@@ -2167,6 +2446,11 @@ public class NotificationManagerService extends SystemService {
return;
}
+ if (isNotificationSpam(notification, pkg)) {
+ mArchive.record(r.sbn);
+ return;
+ }
+
int index = indexOfNotificationLocked(n.getKey());
if (index < 0) {
mNotificationList.add(r);
@@ -2310,6 +2594,21 @@ public class NotificationManagerService extends SystemService {
return false;
}
+ private MediaSessionManager.OnActiveSessionsChangedListener mSessionListener =
+ new MediaSessionManager.OnActiveSessionsChangedListener() {
+ @Override
+ public void onActiveSessionsChanged(@Nullable List<MediaController> controllers) {
+ for (MediaController activeSession : controllers) {
+ PlaybackState playbackState = activeSession.getPlaybackState();
+ if (playbackState != null && playbackState.getState() == PlaybackState.STATE_PLAYING) {
+ mActiveMedia = true;
+ return;
+ }
+ }
+ mActiveMedia = false;
+ }
+ };
+
private void buzzBeepBlinkLocked(NotificationRecord record) {
boolean buzz = false;
boolean beep = false;
@@ -2379,7 +2678,7 @@ public class NotificationManagerService extends SystemService {
hasValidSound = (soundUri != null);
}
- if (hasValidSound) {
+ if (hasValidSound && (!mDisableDuckingWhileMedia || !mActiveMedia)) {
boolean looping =
(notification.flags & Notification.FLAG_INSISTENT) != 0;
AudioAttributes audioAttributes = audioAttributesForNotification(notification);
@@ -2457,7 +2756,8 @@ public class NotificationManagerService extends SystemService {
// light
// release the light
boolean wasShowLights = mLights.remove(record.getKey());
- if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold) {
+ final boolean aboveThresholdWithLight = aboveThreshold || isLedNotificationForcedOn(record);
+ if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThresholdWithLight) {
mLights.add(record.getKey());
updateLightsLocked();
if (mUseAttentionLight) {
@@ -2743,6 +3043,33 @@ public class NotificationManagerService extends SystemService {
return (x < low) ? low : ((x > high) ? high : x);
}
+ private int getSpamCacheHash(CharSequence message, String packageName) {
+ return (message + packageName).hashCode();
+ }
+
+ private boolean isNotificationSpam(Notification notification, String basePkg) {
+ CharSequence normalizedContent = SpamFilter
+ .getNormalizedNotificationContent(notification);
+ int notificationHash = getSpamCacheHash(normalizedContent, basePkg);
+ int notificationId;
+ synchronized (mSpamCache) {
+ notificationId = mSpamCache.get(notificationHash, -1);
+ }
+ if (notificationId != -1) {
+ final String notifIdString = String.valueOf(notificationId);
+ mSpamExecutor.submit(new Runnable() {
+ @Override
+ public void run() {
+ Uri updateUri = Uri.withAppendedPath(UPDATE_MSG_URI,
+ notifIdString);
+ getContext().getContentResolver().update(updateUri,
+ new ContentValues(), null, null);
+ }
+ });
+ }
+ return notificationId != -1;
+ }
+
void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
if (!manager.isEnabled()) {
@@ -3042,6 +3369,16 @@ public class NotificationManagerService extends SystemService {
}
}
+ private boolean isLedNotificationForcedOn(NotificationRecord r) {
+ if (r != null) {
+ final Notification n = r.sbn.getNotification();
+ if (n.extras != null) {
+ return n.extras.getBoolean(Notification.EXTRA_FORCE_SHOW_LIGHTS, false);
+ }
+ }
+ return false;
+ }
+
// lock on mNotificationList
void updateLightsLocked()
{
@@ -3057,29 +3394,101 @@ public class NotificationManagerService extends SystemService {
}
// Don't flash while we are in a call or screen is on
- if (ledNotification == null || mInCall || mScreenOn) {
+ // (unless Notification has EXTRA_FORCE_SHOW_LGHTS)
+ final boolean enableLed;
+ if (ledNotification == null) {
+ enableLed = false;
+ } else if (isLedNotificationForcedOn(ledNotification)) {
+ enableLed = true;
+ } else if (!mScreenOnEnabled && (mInCall || mScreenOn)) {
+ enableLed = false;
+ } else {
+ enableLed = true;
+ }
+
+ if (!enableLed) {
mNotificationLight.turnOff();
mStatusBar.notificationLightOff();
} else {
final Notification ledno = ledNotification.sbn.getNotification();
- int ledARGB = ledno.ledARGB;
- int ledOnMS = ledno.ledOnMS;
- int ledOffMS = ledno.ledOffMS;
- if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
+ final NotificationLedValues ledValues = getLedValuesForNotification(ledNotification);
+ int ledARGB;
+ int ledOnMS;
+ int ledOffMS;
+
+ if (ledValues != null) {
+ ledARGB = ledValues.color != 0 ? ledValues.color : mDefaultNotificationColor;
+ ledOnMS = ledValues.onMS >= 0 ? ledValues.onMS : mDefaultNotificationLedOn;
+ ledOffMS = ledValues.offMS >= 0 ? ledValues.offMS : mDefaultNotificationLedOff;
+ } else if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
ledARGB = mDefaultNotificationColor;
ledOnMS = mDefaultNotificationLedOn;
ledOffMS = mDefaultNotificationLedOff;
+ } else {
+ ledARGB = ledno.ledARGB;
+ ledOnMS = ledno.ledOnMS;
+ ledOffMS = ledno.ledOffMS;
}
+
+ // update the LEDs modes variables
+ mNotificationLight.setModes(mNotificationLedBrightnessLevel,
+ mMultipleLedsEnabledSetting);
+
if (mNotificationPulseEnabled) {
// pulse repeatedly
mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
ledOnMS, ledOffMS);
}
+
// let SystemUI make an independent decision
mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
}
}
+ private void parseNotificationPulseCustomValuesString(String customLedValuesString) {
+ if (TextUtils.isEmpty(customLedValuesString)) {
+ return;
+ }
+
+ for (String packageValuesString : customLedValuesString.split("\\|")) {
+ String[] packageValues = packageValuesString.split("=");
+ if (packageValues.length != 2) {
+ Log.e(TAG, "Error parsing custom led values for unknown package");
+ continue;
+ }
+ String packageName = packageValues[0];
+ String[] values = packageValues[1].split(";");
+ if (values.length != 3) {
+ Log.e(TAG, "Error parsing custom led values '"
+ + packageValues[1] + "' for " + packageName);
+ continue;
+ }
+ NotificationLedValues ledValues = new NotificationLedValues();
+ try {
+ ledValues.color = Integer.parseInt(values[0]);
+ ledValues.onMS = Integer.parseInt(values[1]);
+ ledValues.offMS = Integer.parseInt(values[2]);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Error parsing custom led values '"
+ + packageValues[1] + "' for " + packageName);
+ continue;
+ }
+ mNotificationPulseCustomLedValues.put(packageName, ledValues);
+ }
+ }
+
+ private NotificationLedValues getLedValuesForNotification(NotificationRecord ledNotification) {
+ final String packageName = ledNotification.sbn.getPackageName();
+ return mNotificationPulseCustomLedValues.get(mapPackage(packageName));
+ }
+
+ private String mapPackage(String pkg) {
+ if (!mPackageNameMappings.containsKey(pkg)) {
+ return pkg;
+ }
+ return mPackageNameMappings.get(pkg);
+ }
+
// lock on mNotificationList
int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
{
diff --git a/services/core/java/com/android/server/pm/BasePermission.java b/services/core/java/com/android/server/pm/BasePermission.java
index 52d0928..cab4691 100644
--- a/services/core/java/com/android/server/pm/BasePermission.java
+++ b/services/core/java/com/android/server/pm/BasePermission.java
@@ -53,6 +53,8 @@ final class BasePermission {
*/
private boolean perUser;
+ boolean allowViaWhitelist;
+
BasePermission(String _name, String _sourcePackage, int _type) {
name = _name;
sourcePackage = _sourcePackage;
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index b59b4b2..dba38a3 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -97,14 +97,51 @@ public final class Installer extends SystemService {
debuggable, outputPath);
}
- public int idmap(String targetApkPath, String overlayApkPath, int uid) {
+ public int idmap(String targetApkPath, String overlayApkPath, String cachePath,
+ int uid, int targetHash, int overlayHash) {
StringBuilder builder = new StringBuilder("idmap");
builder.append(' ');
builder.append(targetApkPath);
builder.append(' ');
builder.append(overlayApkPath);
builder.append(' ');
+ builder.append(cachePath);
+ builder.append(' ');
builder.append(uid);
+ builder.append(' ');
+ builder.append(targetHash);
+ builder.append(' ');
+ builder.append(overlayHash);
+ return mInstaller.execute(builder.toString());
+ }
+
+ public int aapt(String themeApkPath, String internalPath, String resTablePath, int uid,
+ int pkgId, int minSdkVersion, String commonResourcesPath) {
+
+ StringBuilder builder = new StringBuilder();
+ if (TextUtils.isEmpty(commonResourcesPath)) {
+ builder.append("aapt");
+ } else {
+ builder.append("aapt_with_common");
+ }
+ builder.append(' ');
+ builder.append(themeApkPath);
+ builder.append(' ');
+ builder.append(internalPath);
+ builder.append(' ');
+ builder.append(resTablePath);
+ builder.append(' ');
+ builder.append(uid);
+ builder.append(' ');
+ builder.append(pkgId);
+ builder.append(' ');
+ builder.append(minSdkVersion);
+
+ if (!TextUtils.isEmpty(commonResourcesPath)) {
+ builder.append(' ');
+ builder.append(commonResourcesPath);
+ }
+
return mInstaller.execute(builder.toString());
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4a473fd..65f5bce 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -381,7 +381,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (stageDir != null && deltaBytes > 0) {
mPm.freeStorage(params.volumeUuid, deltaBytes);
}
- Libcore.os.posix_fallocate(targetFd, 0, lengthBytes);
+ try {
+ Libcore.os.posix_fallocate(targetFd, 0, lengthBytes);
+ } catch (ErrnoException e) {
+ if (e.errno == OsConstants.ENOTSUP) {
+ Libcore.os.ftruncate(targetFd, lengthBytes);
+ } else {
+ throw e.rethrowAsIOException();
+ }
+ }
}
if (offsetBytes > 0) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f49285f..8fcade2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2006 The Android Open Source Project
+ * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +17,7 @@
package com.android.server.pm;
+import static android.Manifest.permission.ACCESS_THEME_MANAGER;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
@@ -42,6 +44,7 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
+import static android.content.pm.PackageManager.INSTALL_FAILED_REGION_LOCKED_PREBUNDLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY;
@@ -64,6 +67,7 @@ import static android.content.pm.PackageManager.MOVE_FAILED_OPERATION_PENDING;
import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.INSTALL_FAILED_UNINSTALLED_PREBUNDLE;
import static android.content.pm.PackageParser.isApkFile;
import static android.os.Process.PACKAGE_INFO_GID;
import static android.os.Process.SYSTEM_UID;
@@ -82,12 +86,22 @@ import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_FAILURE;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
+import static com.android.internal.util.ArrayUtils.removeInt;
+
+import android.content.res.Configuration;
import android.Manifest;
+
+import cyanogenmod.app.suggest.AppSuggestManager;
+
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
+import android.app.ComposedIconInfo;
+import android.app.AppOpsManager;
import android.app.IActivityManager;
+import android.app.IconPackHelper;
+import android.app.PackageInstallObserver;
import android.app.admin.IDevicePolicyManager;
import android.app.backup.IBackupManager;
import android.app.usage.UsageStats;
@@ -129,6 +143,9 @@ import android.content.pm.PackageParser.ActivityIntentInfo;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.PackageStats;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageParser.Activity;
+import android.content.pm.PackageParser.Package;
import android.content.pm.PackageUserState;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
@@ -138,10 +155,15 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
import android.content.pm.UserInfo;
+import android.content.pm.ManifestDigest;
+import android.content.pm.ThemeUtils;
import android.content.pm.VerificationParams;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VerifierInfo;
import android.content.res.Resources;
+import android.content.res.AssetManager;
+import android.content.res.ThemeConfig;
+import android.content.res.ThemeManager;
import android.hardware.display.DisplayManager;
import android.net.Uri;
import android.os.Debug;
@@ -172,6 +194,8 @@ import android.os.storage.StorageEventListener;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord;
+import android.provider.Settings.Global;
+import android.provider.Settings.Secure;
import android.security.KeyStore;
import android.security.SystemKeyStore;
import android.system.ErrnoException;
@@ -188,6 +212,7 @@ import android.util.ExceptionUtils;
import android.util.Log;
import android.util.LogPrinter;
import android.util.MathUtils;
+import android.util.Pair;
import android.util.PrintStreamPrinter;
import android.util.Slog;
import android.util.SparseArray;
@@ -196,6 +221,7 @@ import android.util.SparseIntArray;
import android.util.Xml;
import android.view.Display;
+import cyanogenmod.providers.CMSettings;
import dalvik.system.DexFile;
import dalvik.system.VMRuntime;
@@ -237,16 +263,25 @@ import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.BufferedWriter;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
+import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.IntBuffer;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.CertificateEncodingException;
@@ -268,6 +303,8 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
/**
* Keep track of all those .apks everywhere.
@@ -298,6 +335,7 @@ public class PackageManagerService extends IPackageManager.Stub {
private static final boolean DEBUG_VERIFY = false;
private static final boolean DEBUG_DEXOPT = false;
private static final boolean DEBUG_ABI_SELECTION = false;
+ private static final boolean DEBUG_PREBUNDLED_SCAN = false;
static final boolean CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE = false;
@@ -414,6 +452,21 @@ public class PackageManagerService extends IPackageManager.Stub {
final ServiceThread mHandlerThread;
+ //Where overlays are be found in a theme APK
+ private static final String APK_PATH_TO_OVERLAY = "assets/overlays/";
+
+ //Where the icon pack can be found in a themed apk
+ private static final String APK_PATH_TO_ICONS = "assets/icons/";
+
+ private static final String COMMON_OVERLAY = ThemeUtils.COMMON_RES_TARGET;
+
+ private static final long COMMON_RESOURCE_EXPIRATION = 3*60*1000; // 3 minutes
+
+ /**
+ * The offset in bytes to the beginning of the hashes in an idmap
+ */
+ private static final int IDMAP_HASH_START_OFFSET = 16;
+
final PackageHandler mHandler;
/**
@@ -500,10 +553,13 @@ public class PackageManagerService extends IPackageManager.Stub {
final Settings mSettings;
boolean mRestoredSettings;
+ private Resources mCustomResources;
+
// System configuration read by SystemConfig.
final int[] mGlobalGids;
final SparseArray<ArraySet<String>> mSystemPermissions;
final ArrayMap<String, FeatureInfo> mAvailableFeatures;
+ final ArrayMap<Signature, ArraySet<String>> mSignatureAllowances;
// If mac_permissions.xml was found for seinfo labeling.
boolean mFoundPolicyFile;
@@ -819,6 +875,16 @@ public class PackageManagerService extends IPackageManager.Stub {
private IntentFilterVerifier mIntentFilterVerifier;
+ private IconPackHelper mIconPackHelper;
+
+ private Map<String, Long> mAvailableCommonResources = new ArrayMap<String, Long>();
+
+ private ThemeConfig mBootThemeConfig;
+
+ ArrayList<ComponentName> mDisabledComponentsList;
+
+ private AppOpsManager mAppOps;
+
// Set of pending broadcasts for aggregating enable/disable of components.
static class PendingPackageBroadcasts {
// for each user id, a map of <package name -> components within that package>
@@ -1388,18 +1454,22 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
- packageName, extras, null, null, firstUsers);
+ packageName, null, extras, null, null, firstUsers);
final boolean update = res.removedInfo.removedPackage != null;
if (update) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
+ String category = null;
+ if(res.pkg.mIsThemeApk) {
+ category = Intent.CATEGORY_THEME_PACKAGE_INSTALLED_STATE_CHANGE;
+ }
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
- packageName, extras, null, null, updateUsers);
+ packageName, null, extras, null, null, updateUsers);
if (update) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- packageName, extras, null, null, updateUsers);
+ packageName, null, extras, null, null, updateUsers);
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
- null, null, packageName, null, updateUsers);
+ null, null, null, packageName, null, updateUsers);
// treat asec-hosted packages like removable media on upgrade
if (res.pkg.isForwardLocked() || isExternal(res.pkg)) {
@@ -1414,6 +1484,10 @@ public class PackageManagerService extends IPackageManager.Stub {
pkgList,uidArray, null);
}
}
+ // if this was a theme, send it off to the theme service for processing
+ if(res.pkg.mIsThemeApk || res.pkg.mIsLegacyIconPackApk) {
+ processThemeResourcesInThemeService(res.pkg.packageName);
+ }
if (res.removedInfo.args != null) {
// Remove the replaced package's older resources safely now
deleteOld = true;
@@ -1432,6 +1506,17 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
}
+ if (!update && !isSystemApp(res.pkg)) {
+ boolean privacyGuard = CMSettings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ CMSettings.Secure.PRIVACY_GUARD_DEFAULT,
+ 0, UserHandle.USER_CURRENT) == 1;
+ if (privacyGuard) {
+ mAppOps.setPrivacyGuardSettingForPackage(
+ res.pkg.applicationInfo.uid,
+ res.pkg.applicationInfo.packageName, true);
+ }
+ }
// Log current value of "unknown sources" setting
EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
getUnknownSourcesSettings());
@@ -1832,6 +1917,8 @@ public class PackageManagerService extends IPackageManager.Stub {
}
mDexOptLRUThresholdInMills = dexOptLRUThresholdInMinutes * 60 * 1000;
+ mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+
String separateProcesses = SystemProperties.get("debug.separate_processes");
if (separateProcesses != null && separateProcesses.length() > 0) {
if ("*".equals(separateProcesses)) {
@@ -1858,10 +1945,13 @@ public class PackageManagerService extends IPackageManager.Stub {
getDefaultDisplayMetrics(context, mMetrics);
+ removeLegacyResourceCache();
+
SystemConfig systemConfig = SystemConfig.getInstance();
mGlobalGids = systemConfig.getGlobalGids();
mSystemPermissions = systemConfig.getSystemPermissions();
mAvailableFeatures = systemConfig.getAvailableFeatures();
+ mSignatureAllowances = systemConfig.getSignatureAllowances();
synchronized (mInstallLock) {
// writer
@@ -1907,10 +1997,13 @@ public class PackageManagerService extends IPackageManager.Stub {
mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
- mSdkVersion, mOnlyCore);
+ mSdkVersion, mOnlyCore, mInstaller);
- String customResolverActivity = Resources.getSystem().getString(
- R.string.config_customResolverActivity);
+ String customResolverActivity = SystemProperties.get("ro.custom.resolver.activity");
+ if (TextUtils.isEmpty(customResolverActivity)) {
+ customResolverActivity = Resources.getSystem().getString(
+ R.string.config_customResolverActivity);
+ }
if (TextUtils.isEmpty(customResolverActivity)) {
customResolverActivity = null;
} else {
@@ -2002,6 +2095,10 @@ public class PackageManagerService extends IPackageManager.Stub {
// avoid the resulting log spew.
alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");
+ // Gross hack for now: we know this file doesn't contain any
+ // code, so don't dexopt it to avoid the resulting log spew
+ alreadyDexOpted.add(frameworkDir.getPath() + "/org.cyanogenmod.platform-res.apk");
+
/**
* There are a number of commands implemented in Java, which
* we currently need to do the dexopt on so that they can be
@@ -2056,30 +2153,32 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+ mBootThemeConfig = ThemeConfig.getSystemTheme();
+
// Collect vendor overlay packages.
// (Do this before scanning any apps.)
// For security and version matching reason, only consider
// overlay packages if they reside in VENDOR_OVERLAY_DIR.
File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
+ | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0, null);
// Find base frameworks (resource packages without code).
scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED,
- scanFlags | SCAN_NO_DEX, 0);
+ scanFlags | SCAN_NO_DEX, 0, null);
// Collected privileged system packages.
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
- | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);
+ | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0, null);
// Collect ordinary system packages.
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
+ | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0, null);
// Collect all vendor packages.
File vendorAppDir = new File("/vendor/app");
@@ -2089,12 +2188,19 @@ public class PackageManagerService extends IPackageManager.Stub {
// failed to look up canonical path, continue with original one
}
scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
+ | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0, null);
// Collect all OEM packages.
final File oemAppDir = new File(Environment.getOemDirectory(), "app");
scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
+ | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0, null);
+
+ // Collect all prebundled packages.
+ createAndSetCustomResources();
+ scanDirLI(Environment.getPrebundledDirectory(),
+ PackageParser.PARSE_IS_PREBUNDLED_DIR, scanFlags, 0, UserHandle.OWNER);
+ // Clean up
+ mCustomResources = null;
if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
mInstaller.moveFiles();
@@ -2169,10 +2275,10 @@ public class PackageManagerService extends IPackageManager.Stub {
if (!mOnlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
- scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
+ scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0, null);
scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
- scanFlags | SCAN_REQUIRE_KNOWN, 0);
+ scanFlags | SCAN_REQUIRE_KNOWN, 0, null);
/**
* Remove disable package settings for any updated system
@@ -2284,6 +2390,12 @@ public class PackageManagerService extends IPackageManager.Stub {
updatePermissionsLPw(null, null, updateFlags);
ver.sdkVersion = mSdkVersion;
+ // Remove any stale app permissions (declared permission that now are undeclared
+ // by the same app, removed from its Manifest in newer versions)
+ if (!onlyCore) {
+ mSettings.removeStalePermissions();
+ }
+
// If this is the first boot or an update from pre-M, and it is a normal
// boot, then we need to initialize the default preferred apps across
// all defined users.
@@ -2295,6 +2407,38 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+ // Disable components marked for disabling at build-time
+ mDisabledComponentsList = new ArrayList<ComponentName>();
+ for (String name : mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_disabledComponents)) {
+ ComponentName cn = ComponentName.unflattenFromString(name);
+ mDisabledComponentsList.add(cn);
+ Slog.v(TAG, "Disabling " + name);
+ String className = cn.getClassName();
+ PackageSetting pkgSetting = mSettings.mPackages.get(cn.getPackageName());
+ if (pkgSetting == null || pkgSetting.pkg == null
+ || !pkgSetting.pkg.hasComponentClassName(className)) {
+ Slog.w(TAG, "Unable to disable " + name);
+ continue;
+ }
+ pkgSetting.disableComponentLPw(className, UserHandle.USER_OWNER);
+ }
+
+ // Enable components marked for forced-enable at build-time
+ for (String name : mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_forceEnabledComponents)) {
+ ComponentName cn = ComponentName.unflattenFromString(name);
+ Slog.v(TAG, "Enabling " + name);
+ String className = cn.getClassName();
+ PackageSetting pkgSetting = mSettings.mPackages.get(cn.getPackageName());
+ if (pkgSetting == null || pkgSetting.pkg == null
+ || !pkgSetting.pkg.hasComponentClassName(className)) {
+ Slog.w(TAG, "Unable to enable " + name);
+ continue;
+ }
+ pkgSetting.enableComponentLPw(className, UserHandle.USER_OWNER);
+ }
+
// If this is first boot after an OTA, and a normal boot, then
// we need to clear code cache directories.
if (mIsUpgrade && !onlyCore) {
@@ -3326,6 +3470,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false;
// We'll take care of setting this one.
if (!compareStrings(pi1.packageName, pi2.packageName)) return false;
+ if (pi1.allowViaWhitelist != pi2.allowViaWhitelist) return false;
// These are not currently stored in settings.
//if (!compareStrings(pi1.group, pi2.group)) return false;
//if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false;
@@ -3450,6 +3595,16 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+ private boolean isAllowedSignature(PackageParser.Package pkg, String permissionName) {
+ for (Signature pkgSig : pkg.mSignatures) {
+ ArraySet<String> perms = mSignatureAllowances.get(pkgSig);
+ if (perms != null && perms.contains(permissionName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
public void grantRuntimePermission(String packageName, String name, final int userId) {
if (!sUserManager.exists(userId)) {
@@ -3943,7 +4098,7 @@ public class PackageManagerService extends IPackageManager.Stub {
return PackageManager.SIGNATURE_NO_MATCH;
}
- // Since both signature sets are of size 1, we can compare without HashSets.
+ // Since both signature sets are of size 1, we can compare without ArraySets.
if (s1.length == 1) {
return s1[0].equals(s2[0]) ?
PackageManager.SIGNATURE_MATCH :
@@ -4246,6 +4401,17 @@ public class PackageManagerService extends IPackageManager.Stub {
if (ri.activityInfo.metaData == null) ri.activityInfo.metaData = new Bundle();
ri.activityInfo.metaData.putBoolean(Intent.METADATA_DOCK_HOME, true);
return ri;
+ } else if (shouldIncludeResolveActivity(intent)) {
+ if (userId != 0) {
+ ResolveInfo ri = new ResolveInfo(mResolveInfo);
+ ri.activityInfo = new ActivityInfo(ri.activityInfo);
+ ri.activityInfo.applicationInfo = new ApplicationInfo(
+ ri.activityInfo.applicationInfo);
+ ri.activityInfo.applicationInfo.uid = UserHandle.getUid(userId,
+ UserHandle.getAppId(ri.activityInfo.applicationInfo.uid));
+ return ri;
+ }
+ return mResolveInfo;
}
}
return null;
@@ -4506,6 +4672,13 @@ public class PackageManagerService extends IPackageManager.Stub {
return null;
}
+ private boolean shouldIncludeResolveActivity(Intent intent) {
+ synchronized(mPackages) {
+ AppSuggestManager suggest = AppSuggestManager.getInstance(mContext);
+ return mResolverReplaced && (suggest != null) ? suggest.handles(intent) : false;
+ }
+ }
+
@Override
public List<ResolveInfo> queryIntentActivities(Intent intent,
String resolvedType, int flags, int userId) {
@@ -5571,11 +5744,17 @@ public class PackageManagerService extends IPackageManager.Stub {
private boolean createIdmapForPackagePairLI(PackageParser.Package pkg,
PackageParser.Package opkg) {
+ if (DEBUG_PACKAGE_SCANNING) Log.d(TAG,
+ "Generating idmaps between " + pkg.packageName + ":" + opkg.packageName);
if (!opkg.mTrustedOverlay) {
Slog.w(TAG, "Skipping target and overlay pair " + pkg.baseCodePath + " and " +
opkg.baseCodePath + ": overlay not trusted");
return false;
}
+
+ // Some apps like to take on the package name of an existing app so we'll use the
+ // "real" package name, if it is non-null, when performing the idmap
+ final String pkgName = pkg.mRealPackage != null ? pkg.mRealPackage : pkg.packageName;
ArrayMap<String, PackageParser.Package> overlaySet = mOverlays.get(pkg.packageName);
if (overlaySet == null) {
Slog.e(TAG, "was about to create idmap for " + pkg.baseCodePath + " and " +
@@ -5583,30 +5762,19 @@ public class PackageManagerService extends IPackageManager.Stub {
return false;
}
final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
- // TODO: generate idmap for split APKs
- if (mInstaller.idmap(pkg.baseCodePath, opkg.baseCodePath, sharedGid) != 0) {
- Slog.e(TAG, "Failed to generate idmap for " + pkg.baseCodePath + " and "
- + opkg.baseCodePath);
+ final String cachePath =
+ ThemeUtils.getTargetCacheDir(pkgName, opkg.packageName);
+ if (mInstaller.idmap(pkg.baseCodePath, opkg.baseCodePath, cachePath, sharedGid,
+ pkg.manifestHashCode, opkg.manifestHashCode) != 0) {
+ Slog.e(TAG, "Failed to generate idmap for " + pkg.baseCodePath +
+ " and " + opkg.baseCodePath);
return false;
}
- PackageParser.Package[] overlayArray =
- overlaySet.values().toArray(new PackageParser.Package[0]);
- Comparator<PackageParser.Package> cmp = new Comparator<PackageParser.Package>() {
- public int compare(PackageParser.Package p1, PackageParser.Package p2) {
- return p1.mOverlayPriority - p2.mOverlayPriority;
- }
- };
- Arrays.sort(overlayArray, cmp);
-
- pkg.applicationInfo.resourceDirs = new String[overlayArray.length];
- int i = 0;
- for (PackageParser.Package p : overlayArray) {
- pkg.applicationInfo.resourceDirs[i++] = p.baseCodePath;
- }
return true;
}
- private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
+ private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime,
+ UserHandle user) {
final File[] files = dir.listFiles();
if (ArrayUtils.isEmpty(files)) {
Log.d(TAG, "No files in app dir " + dir);
@@ -5618,6 +5786,16 @@ public class PackageManagerService extends IPackageManager.Stub {
+ " flags=0x" + Integer.toHexString(parseFlags));
}
+ int prebundledUserId = user == null ? UserHandle.USER_OWNER : user.getIdentifier();
+ boolean isPrebundled = (parseFlags & PackageParser.PARSE_IS_PREBUNDLED_DIR) != 0;
+ if (isPrebundled) {
+ synchronized (mPackages) {
+ if (DEBUG_PREBUNDLED_SCAN) Log.d(TAG, "Reading prebundled packages for user "
+ + prebundledUserId);
+ mSettings.readPrebundledPackagesLPr(prebundledUserId);
+ }
+ }
+
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
@@ -5627,7 +5805,27 @@ public class PackageManagerService extends IPackageManager.Stub {
}
try {
scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
- scanFlags, currentTime, null);
+ scanFlags, currentTime, user);
+ if (isPrebundled) {
+ final PackageParser.Package pkg;
+ try {
+ pkg = new PackageParser().parsePackage(file, parseFlags);
+ } catch (PackageParserException e) {
+ throw PackageManagerException.from(e);
+ }
+ synchronized (mPackages) {
+ if (DEBUG_PREBUNDLED_SCAN) Log.d(TAG,
+ "Marking prebundled packages for user " + prebundledUserId);
+ mSettings.markPrebundledPackageInstalledLPr(prebundledUserId,
+ pkg.packageName);
+ // do this for every other user
+ for (UserInfo userInfo : sUserManager.getUsers(true)) {
+ if (userInfo.id == prebundledUserId) continue;
+ mSettings.markPrebundledPackageInstalledLPr(userInfo.id,
+ pkg.packageName);
+ }
+ }
+ }
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());
@@ -5643,6 +5841,14 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
}
+
+ if (isPrebundled) {
+ synchronized (mPackages) {
+ if (DEBUG_PREBUNDLED_SCAN) Log.d(TAG, "Writing prebundled packages for user "
+ + prebundledUserId);
+ mSettings.writePrebundledPackagesLPr(prebundledUserId);
+ }
+ }
}
private static File getSettingsProblemFile() {
@@ -5696,6 +5902,12 @@ public class PackageManagerService extends IPackageManager.Stub {
// if the package appears to be unchanged.
pkg.mSignatures = ps.signatures.mSignatures;
pkg.mSigningKeys = signingKs;
+ // Collect manifest digest
+ try{
+ pp.collectManifestDigest(pkg);
+ } catch (PackageParserException e) {
+ throw PackageManagerException.from(e);
+ }
return;
}
@@ -5715,7 +5927,7 @@ public class PackageManagerService extends IPackageManager.Stub {
/*
* Scan a package and return the newly parsed package.
- * Returns null in case of errors and the error code is stored in mLastScanError
+ * Returns null in case of errors and the error code is stored in
*/
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
@@ -5737,6 +5949,36 @@ public class PackageManagerService extends IPackageManager.Stub {
throw PackageManagerException.from(e);
}
+ if ((parseFlags & PackageParser.PARSE_IS_PREBUNDLED_DIR) != 0) {
+ synchronized (mPackages) {
+ PackageSetting existingSettings = mSettings.peekPackageLPr(pkg.packageName);
+ if (mSettings.wasPrebundledPackageInstalledLPr(user.getIdentifier()
+ , pkg.packageName) && existingSettings == null) {
+ // The prebundled app was installed at some point in time, but now it is
+ // gone. Assume that the user uninstalled it intentionally: do not reinstall.
+ throw new PackageManagerException(INSTALL_FAILED_UNINSTALLED_PREBUNDLE,
+ "skip reinstall for " + pkg.packageName);
+ } else if (existingSettings == null &&
+ !mSettings.shouldPrebundledPackageBeInstalled(mContext.getResources(),
+ pkg.packageName, mCustomResources)) {
+ // The prebundled app is not needed for the default mobile country code,
+ // skip installing it
+ throw new PackageManagerException(INSTALL_FAILED_REGION_LOCKED_PREBUNDLE,
+ "skip install for " + pkg.packageName);
+ } else if (existingSettings != null
+ && existingSettings.versionCode >= pkg.mVersionCode
+ && !existingSettings.codePathString.contains(
+ Environment.getPrebundledDirectory().getPath())) {
+ // This app is installed in a location that is not the prebundled location
+ // and has a higher (or same) version as the prebundled one. Skip
+ // installing the prebundled version.
+ Slog.d(TAG, pkg.packageName + " already installed at " +
+ existingSettings.codePathString);
+ return null; // return null so we still mark package as installed
+ }
+ }
+ }
+
PackageSetting ps = null;
PackageSetting updatedPkg;
// reader
@@ -6197,13 +6439,11 @@ public class PackageManagerService extends IPackageManager.Stub {
if (DEBUG_DEXOPT) {
Log.i(TAG, "Optimizing app " + curr + " of " + total + ": " + pkg.packageName);
}
- if (!isFirstBoot()) {
- try {
- ActivityManagerNative.getDefault().showBootMessage(
- mContext.getResources().getString(R.string.android_upgrading_apk,
- curr, total), true);
- } catch (RemoteException e) {
- }
+ try {
+ ActivityManagerNative.getDefault().showBootMessage(
+ mContext.getResources().getString(R.string.android_upgrading_apk,
+ curr, total), true);
+ } catch (RemoteException e) {
}
PackageParser.Package p = pkg;
synchronized (mInstallLock) {
@@ -6601,6 +6841,20 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+ if (Build.TAGS.equals("test-keys") &&
+ !pkg.applicationInfo.sourceDir.startsWith(Environment.getRootDirectory().getPath()) &&
+ !pkg.applicationInfo.sourceDir.startsWith("/vendor")) {
+ Object obj = mSettings.getUserIdLPr(1000);
+ Signature[] s1 = null;
+ if (obj instanceof SharedUserSetting) {
+ s1 = ((SharedUserSetting)obj).signatures.mSignatures;
+ }
+ if ((compareSignatures(pkg.mSignatures, s1) == PackageManager.SIGNATURE_MATCH)) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+ "Cannot install platform packages to user storage!");
+ }
+ }
+
// Initialize package source and resource directories
File destCodeFile = new File(pkg.applicationInfo.getCodePath());
File destResourceFile = new File(pkg.applicationInfo.getResourcePath());
@@ -6615,6 +6869,16 @@ public class PackageManagerService extends IPackageManager.Stub {
pkg.mAdoptPermissions = null;
}
+ // collect manifest digest which includes getting manifest hash code for themes
+ if (pkg.manifestDigest == null || pkg.manifestHashCode == 0) {
+ PackageParser pp = new PackageParser();
+ try {
+ pp.collectManifestDigest(pkg);
+ } catch (PackageParserException e) {
+ Slog.w(TAG, "Unable to collect manifest digest", e);
+ }
+ }
+
// writer
synchronized (mPackages) {
if (pkg.mSharedUserId != null) {
@@ -6980,7 +7244,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
- derivePackageAbi(pkg, scanFile, cpuAbiOverride, true /* extract libs */);
+ derivePackageAbi(pkg, scanFile, cpuAbiOverride, true /* extract libs */, parseFlags);
// Some system apps still use directory structure for native libraries
// in which case we might end up not detecting abi solely based on apk
@@ -6988,7 +7252,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() &&
pkg.applicationInfo.primaryCpuAbi == null) {
setBundledAppAbisAndRoots(pkg, pkgSetting);
- setNativeLibraryPaths(pkg);
+ setNativeLibraryPaths(pkg, parseFlags);
}
} else {
@@ -7004,7 +7268,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// ABIs we've determined above. For non-moves, the path will be updated based on the
// ABIs we determined during compilation, but the path will depend on the final
// package path (after the rename away from the stage path).
- setNativeLibraryPaths(pkg);
+ setNativeLibraryPaths(pkg, parseFlags);
}
if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path);
@@ -7207,6 +7471,13 @@ public class PackageManagerService extends IPackageManager.Stub {
// Add the new setting to mSettings
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
+
+ // Themes: handle case where app was installed after icon mapping applied
+ if (mIconPackHelper != null) {
+ int id = mIconPackHelper.getResourceIdForApp(pkg.packageName);
+ pkg.applicationInfo.themedIcon = id;
+ }
+
// Add the new setting to mPackages
mPackages.put(pkg.applicationInfo.packageName, pkg);
// Make sure we don't accidentally delete its data.
@@ -7345,6 +7616,13 @@ public class PackageManagerService extends IPackageManager.Stub {
PackageParser.Activity a = pkg.activities.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
a.info.processName, pkg.applicationInfo.uid);
+
+ // Themes: handle case where app was installed after icon mapping applied
+ if (mIconPackHelper != null) {
+ a.info.themedIcon = mIconPackHelper
+ .getResourceIdForActivityIcon(a.info);
+ }
+
mActivities.addActivity(a, "activity");
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r == null) {
@@ -7430,6 +7708,7 @@ public class PackageManagerService extends IPackageManager.Stub {
bp.perm = p;
bp.uid = pkg.applicationInfo.uid;
bp.sourcePackage = p.info.packageName;
+ bp.allowViaWhitelist = p.info.allowViaWhitelist;
p.info.flags |= PermissionInfo.FLAG_INSTALLED;
} else if (!currentOwnerIsSystem) {
String msg = "New decl " + p.owner + " of permission "
@@ -7443,6 +7722,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (bp == null) {
bp = new BasePermission(p.info.name, p.info.packageName,
BasePermission.TYPE_NORMAL);
+ bp.allowViaWhitelist = p.info.allowViaWhitelist;
permissionMap.put(p.info.name, bp);
}
@@ -7456,6 +7736,7 @@ public class PackageManagerService extends IPackageManager.Stub {
bp.perm = p;
bp.uid = pkg.applicationInfo.uid;
bp.sourcePackage = p.info.packageName;
+ bp.allowViaWhitelist = p.info.allowViaWhitelist;
p.info.flags |= PermissionInfo.FLAG_INSTALLED;
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r == null) {
@@ -7531,27 +7812,85 @@ public class PackageManagerService extends IPackageManager.Stub {
pkgSetting.setTimeStamp(scanFileTime);
- // Create idmap files for pairs of (packages, overlay packages).
- // Note: "android", ie framework-res.apk, is handled by native layers.
- if (pkg.mOverlayTarget != null) {
- // This is an overlay package.
- if (pkg.mOverlayTarget != null && !pkg.mOverlayTarget.equals("android")) {
- if (!mOverlays.containsKey(pkg.mOverlayTarget)) {
- mOverlays.put(pkg.mOverlayTarget,
- new ArrayMap<String, PackageParser.Package>());
+ final boolean isBootScan = (scanFlags & SCAN_BOOTING) != 0;
+ // Generate resources & idmaps if pkg is NOT a theme
+ // We must compile resources here because during the initial boot process we may get
+ // here before a default theme has had a chance to compile its resources
+ if (pkg.mOverlayTargets.isEmpty() && mOverlays.containsKey(pkg.packageName)) {
+ ArrayMap<String, PackageParser.Package> themes = mOverlays.get(pkg.packageName);
+ for(PackageParser.Package themePkg : themes.values()) {
+ if (!isBootScan || (mBootThemeConfig != null &&
+ (themePkg.packageName.equals(mBootThemeConfig.getOverlayPkgName()) ||
+ themePkg.packageName.equals(
+ mBootThemeConfig.getOverlayPkgNameForApp(pkg.packageName))))) {
+ try {
+ compileResourcesAndIdmapIfNeeded(pkg, themePkg);
+ } catch (Exception e) {
+ // Do not stop a pkg installation just because of one bad theme
+ // Also we don't break here because we should try to compile other
+ // themes
+ Log.e(TAG, "Unable to compile " + themePkg.packageName
+ + " for target " + pkg.packageName, e);
+ }
}
- ArrayMap<String, PackageParser.Package> map = mOverlays.get(pkg.mOverlayTarget);
- map.put(pkg.packageName, pkg);
- PackageParser.Package orig = mPackages.get(pkg.mOverlayTarget);
- if (orig != null && !createIdmapForPackagePairLI(orig, pkg)) {
- throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "scanPackageLI failed to createIdmap");
+ }
+ }
+
+ // If this is a theme we should re-compile common resources if they exist so
+ // remove this package from mAvailableCommonResources.
+ if (!isBootScan && pkg.mOverlayTargets.size() > 0) {
+ mAvailableCommonResources.remove(pkg.packageName);
+ }
+
+ // Generate Idmaps and res tables if pkg is a theme
+ for(String target : pkg.mOverlayTargets) {
+ Exception failedException = null;
+ int failReason = 0;
+
+ insertIntoOverlayMap(target, pkg);
+ if (isBootScan && mBootThemeConfig != null &&
+ (pkg.packageName.equals(mBootThemeConfig.getOverlayPkgName()) ||
+ pkg.packageName.equals(
+ mBootThemeConfig.getOverlayPkgNameForApp(target)))) {
+ try {
+ compileResourcesAndIdmapIfNeeded(mPackages.get(target), pkg);
+ } catch (IdmapException e) {
+ failedException = e;
+ failReason = PackageManager.INSTALL_FAILED_THEME_IDMAP_ERROR;
+ } catch (AaptException e) {
+ failedException = e;
+ failReason = PackageManager.INSTALL_FAILED_THEME_AAPT_ERROR;
+ } catch (Exception e) {
+ failedException = e;
+ failReason = PackageManager.INSTALL_FAILED_THEME_UNKNOWN_ERROR;
+ }
+
+ if (failedException != null) {
+ Log.w(TAG, "Unable to process theme " + pkgName, failedException);
+ uninstallThemeForAllApps(pkg);
+ deletePackageLI(pkg.packageName, null, true, null, null, 0, null, false);
+ throw new PackageManagerException(failReason,
+ "Unable to process theme " + pkgName, failedException);
+ }
+ }
+ }
+
+ //Icon Packs need aapt too
+ if (isBootScan && (mBootThemeConfig != null &&
+ pkg.packageName.equals(mBootThemeConfig.getIconPackPkgName()))) {
+ if (isIconCompileNeeded(pkg)) {
+ try {
+ ThemeUtils.createCacheDirIfNotExists();
+ ThemeUtils.createIconDirIfNotExists(pkg.packageName);
+ compileIconPack(pkg);
+ } catch (Exception e) {
+ uninstallThemeForAllApps(pkg);
+ deletePackageLI(pkg.packageName, null, true, null, null, 0, null, false);
+ throw new PackageManagerException(
+ PackageManager.INSTALL_FAILED_THEME_AAPT_ERROR,
+ "Unable to process theme " + pkgName);
}
}
- } else if (mOverlays.containsKey(pkg.packageName) &&
- !pkg.packageName.equals("android")) {
- // This is a regular package, with one or more known overlay packages.
- createIdmapsForPackageLI(pkg);
}
}
@@ -7566,7 +7905,8 @@ public class PackageManagerService extends IPackageManager.Stub {
* If {@code extractLibs} is true, native libraries are extracted from the app if required.
*/
public void derivePackageAbi(PackageParser.Package pkg, File scanFile,
- String cpuAbiOverride, boolean extractLibs)
+ String cpuAbiOverride, boolean extractLibs,
+ int parseFlags)
throws PackageManagerException {
// TODO: We can probably be smarter about this stuff. For installed apps,
// we can calculate this information at install time once and for all. For
@@ -7575,7 +7915,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// Give ourselves some initial paths; we'll come back for another
// pass once we've determined ABI below.
- setNativeLibraryPaths(pkg);
+ setNativeLibraryPaths(pkg, parseFlags);
// We would never need to extract libs for forward-locked and external packages,
// since the container service will do it for us. We shouldn't attempt to
@@ -7695,7 +8035,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// Now that we've calculated the ABIs and determined if it's an internal app,
// we will go ahead and populate the nativeLibraryPath.
- setNativeLibraryPaths(pkg);
+ setNativeLibraryPaths(pkg, parseFlags);
}
/**
@@ -7791,6 +8131,305 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+
+ private boolean isIconCompileNeeded(Package pkg) {
+ if (!pkg.hasIconPack) return false;
+ // Read in the stored hash value and compare to the pkgs computed hash value
+ FileInputStream in = null;
+ DataInputStream dataInput = null;
+ try {
+ String hashFile = ThemeUtils.getIconHashFile(pkg.packageName);
+ in = new FileInputStream(hashFile);
+ dataInput = new DataInputStream(in);
+ int storedHashCode = dataInput.readInt();
+ int actualHashCode = pkg.manifestHashCode;
+ return storedHashCode != actualHashCode;
+ } catch(IOException e) {
+ // all is good enough for government work here,
+ // we'll just return true and the icons will be processed
+ } finally {
+ IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(dataInput);
+ }
+
+ return true;
+ }
+
+ private void compileResourcesAndIdmapIfNeeded(PackageParser.Package targetPkg,
+ PackageParser.Package themePkg)
+ throws IdmapException, AaptException, IOException, Exception
+ {
+ if (!shouldCreateIdmap(targetPkg, themePkg)) {
+ return;
+ }
+
+ // Always use the manifest's pkgName when compiling resources
+ // the member value of "packageName" is dependent on whether this was a clean install
+ // or an upgrade w/ If the app is an upgrade then the original package name is used.
+ // because libandroidfw uses the manifests's pkgName during idmap creation we must
+ // be consistent here and use the same name, otherwise idmap will look in the wrong place
+ // for the resource table.
+ String pkgName = targetPkg.mRealPackage != null ?
+ targetPkg.mRealPackage : targetPkg.packageName;
+ compileResourcesIfNeeded(pkgName, themePkg);
+ generateIdmap(targetPkg.packageName, themePkg);
+ }
+
+ private void compileResourcesIfNeeded(String target, PackageParser.Package pkg)
+ throws AaptException, IOException, Exception
+ {
+ ThemeUtils.createCacheDirIfNotExists();
+
+ if (hasCommonResources(pkg)
+ && shouldCompileCommonResources(pkg)) {
+ ThemeUtils.createResourcesDirIfNotExists(COMMON_OVERLAY, pkg.packageName);
+ compileResources(COMMON_OVERLAY, pkg);
+ mAvailableCommonResources.put(pkg.packageName, System.currentTimeMillis());
+ }
+
+ ThemeUtils.createResourcesDirIfNotExists(target, pkg.packageName);
+ compileResources(target, pkg);
+ }
+
+ private void compileResources(String target, PackageParser.Package pkg) throws Exception {
+ if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Compile resource table for " + pkg.packageName);
+ //TODO: cleanup this hack. Modify aapt? Aapt uses the manifests package name
+ //when creating the resource table. We care about the resource table's name because
+ //it is used when removing the table by cookie.
+ try {
+ createTempManifest(COMMON_OVERLAY.equals(target)
+ ? ThemeUtils.getCommonPackageName(pkg.packageName) : pkg.packageName);
+ compileResourcesWithAapt(target, pkg);
+ } finally {
+ cleanupTempManifest();
+ }
+ }
+
+ private void compileIconPack(Package pkg) throws Exception {
+ if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Compile resource table for " + pkg.packageName);
+ OutputStream out = null;
+ DataOutputStream dataOut = null;
+ try {
+ createTempManifest(pkg.packageName);
+ int code = pkg.manifestHashCode;
+ String hashFile = ThemeUtils.getIconHashFile(pkg.packageName);
+ out = new FileOutputStream(hashFile);
+ dataOut = new DataOutputStream(out);
+ dataOut.writeInt(code);
+ compileIconsWithAapt(pkg);
+ } finally {
+ IoUtils.closeQuietly(out);
+ IoUtils.closeQuietly(dataOut);
+ cleanupTempManifest();
+ }
+ }
+
+ private void insertIntoOverlayMap(String target, PackageParser.Package opkg) {
+ if (!mOverlays.containsKey(target)) {
+ mOverlays.put(target,
+ new ArrayMap<String, PackageParser.Package>());
+ }
+ ArrayMap<String, PackageParser.Package> map = mOverlays.get(target);
+ map.put(opkg.packageName, opkg);
+ }
+
+ private void generateIdmap(String target, PackageParser.Package opkg) throws IdmapException {
+ PackageParser.Package targetPkg = mPackages.get(target);
+ if (targetPkg != null && !createIdmapForPackagePairLI(targetPkg, opkg)) {
+ throw new IdmapException("idmap failed for targetPkg: " + targetPkg
+ + " and opkg: " + opkg);
+ }
+ }
+
+ public class AaptException extends Exception {
+ public AaptException(String message) {
+ super(message);
+ }
+ }
+
+ public class IdmapException extends Exception {
+ public IdmapException(String message) {
+ super(message);
+ }
+ }
+
+ private boolean hasCommonResources(PackageParser.Package pkg) throws Exception {
+ boolean ret = false;
+ // check if assets/overlays/common exists in this theme
+ AssetManager assets = new AssetManager();
+ assets.addAssetPath(pkg.baseCodePath);
+ String[] common = assets.list("overlays/common");
+ if (common != null && common.length > 0) ret = true;
+
+ return ret;
+ }
+
+ private void compileResourcesWithAapt(String target, PackageParser.Package pkg)
+ throws Exception {
+ String internalPath = APK_PATH_TO_OVERLAY + target + File.separator;
+ String resPath = ThemeUtils.getTargetCacheDir(target, pkg);
+ final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+ int pkgId;
+ if ("android".equals(target)) {
+ pkgId = Resources.THEME_FRAMEWORK_PKG_ID;
+ } else if (COMMON_OVERLAY.equals(target)) {
+ pkgId = Resources.THEME_COMMON_PKG_ID;
+ } else {
+ pkgId = Resources.THEME_APP_PKG_ID;
+ }
+
+ boolean hasCommonResources = (hasCommonResources(pkg) && !COMMON_OVERLAY.equals(target));
+ if (mInstaller.aapt(pkg.baseCodePath, internalPath, resPath, sharedGid, pkgId,
+ pkg.applicationInfo.targetSdkVersion,
+ hasCommonResources ? ThemeUtils.getTargetCacheDir(COMMON_OVERLAY, pkg)
+ + File.separator + "resources.apk" : "") != 0) {
+ throw new AaptException("Failed to run aapt");
+ }
+ }
+
+ private void compileIconsWithAapt(Package pkg) throws Exception {
+ String resPath = ThemeUtils.getIconPackDir(pkg.packageName);
+ final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+
+ if (mInstaller.aapt(pkg.baseCodePath, APK_PATH_TO_ICONS, resPath, sharedGid,
+ Resources.THEME_ICON_PKG_ID,
+ pkg.applicationInfo.targetSdkVersion,
+ "") != 0) {
+ throw new AaptException("Failed to run aapt");
+ }
+ }
+
+ private void uninstallThemeForAllApps(PackageParser.Package opkg) {
+ for(String target : opkg.mOverlayTargets) {
+ ArrayMap<String, PackageParser.Package> map = mOverlays.get(target);
+ if (map != null) {
+ map.remove(opkg.packageName);
+
+ if (map.isEmpty()) {
+ mOverlays.remove(target);
+ }
+ }
+ }
+
+ // Now simply delete the root overlay cache directory and all its contents
+ recursiveDelete(new File(ThemeUtils.getOverlayResourceCacheDir(opkg.packageName)));
+ }
+
+ private void uninstallThemeForApp(PackageParser.Package appPkg) {
+ ArrayMap<String, PackageParser.Package> map = mOverlays.get(appPkg.packageName);
+ if (map == null) return;
+
+ for(PackageParser.Package opkg : map.values()) {
+ String cachePath = ThemeUtils.getTargetCacheDir(appPkg.packageName, opkg.packageName);
+ recursiveDelete(new File(cachePath));
+ }
+ }
+
+ private void recursiveDelete(File f) {
+ if (f.isDirectory()) {
+ for (File c : f.listFiles())
+ recursiveDelete(c);
+ }
+ f.delete();
+ }
+
+ private void createTempManifest(String pkgName) throws Exception {
+ StringBuilder manifest = new StringBuilder();
+ manifest.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
+ manifest.append("<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"");
+ manifest.append(" package=\"" + pkgName+ "\">");
+ manifest.append(" </manifest>");
+
+ BufferedWriter bw = null;
+ try {
+ bw = new BufferedWriter(new FileWriter("/data/app/AndroidManifest.xml"));
+ bw.write(manifest.toString());
+ bw.flush();
+ bw.close();
+ File resFile = new File("/data/app/AndroidManifest.xml");
+ FileUtils.setPermissions(resFile,
+ FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IROTH, -1, -1);
+ } finally {
+ IoUtils.closeQuietly(bw);
+ }
+ }
+
+ private void cleanupTempManifest() {
+ File resFile = new File("/data/app/AndroidManifest.xml");
+ resFile.delete();
+ }
+
+ /**
+ * Compares the 32 bit hash of the target and overlay to those stored
+ * in the idmap and returns true if either hash differs
+ * @param targetPkg
+ * @param overlayPkg
+ * @return
+ */
+ private boolean shouldCreateIdmap(PackageParser.Package targetPkg,
+ PackageParser.Package overlayPkg) {
+ if (targetPkg == null || targetPkg.baseCodePath == null || overlayPkg == null) return false;
+
+ int targetHash = targetPkg.manifestHashCode;
+ int overlayHash = overlayPkg.manifestHashCode;
+
+ File idmap =
+ new File(ThemeUtils.getIdmapPath(targetPkg.packageName, overlayPkg.packageName));
+ if (!idmap.exists()) {
+ return true;
+ }
+
+ int[] hashes;
+ try {
+ hashes = getIdmapHashes(idmap);
+ } catch (IOException e) {
+ return true;
+ }
+
+ if (targetHash == 0 || overlayHash == 0 ||
+ targetHash != hashes[0] || overlayHash != hashes[1]) {
+ // if the overlay changed we'll want to recreate the common resources if it has any
+ if (overlayHash != hashes[1]
+ && mAvailableCommonResources.containsKey(overlayPkg.packageName)) {
+ mAvailableCommonResources.remove(overlayPkg.packageName);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private boolean shouldCompileCommonResources(PackageParser.Package pkg) {
+ if (!mAvailableCommonResources.containsKey(pkg.packageName)) return true;
+
+ long lastUpdated = mAvailableCommonResources.get(pkg.packageName);
+ long currentTime = System.currentTimeMillis();
+ if (currentTime - lastUpdated > COMMON_RESOURCE_EXPIRATION) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Get the file modified times for the overlay and target from the idmap
+ * @param idmap
+ * @return
+ * @throws IOException
+ */
+ private int[] getIdmapHashes(File idmap) throws IOException {
+ int[] times = new int[2];
+ ByteBuffer bb = ByteBuffer.allocate(8);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+ FileInputStream fis = new FileInputStream(idmap);
+ fis.skip(IDMAP_HASH_START_OFFSET);
+ fis.read(bb.array());
+ fis.close();
+ final IntBuffer ib = bb.asIntBuffer();
+ times[0] = ib.get(0);
+ times[1] = ib.get(1);
+
+ return times;
+ }
+
private void setUpCustomResolverActivity(PackageParser.Package pkg) {
synchronized (mPackages) {
mResolverReplaced = true;
@@ -7851,7 +8490,7 @@ public class PackageManagerService extends IPackageManager.Stub {
* Derive and set the location of native libraries for the given package,
* which varies depending on where and how the package was installed.
*/
- private void setNativeLibraryPaths(PackageParser.Package pkg) {
+ private void setNativeLibraryPaths(PackageParser.Package pkg, int parseFlags) {
final ApplicationInfo info = pkg.applicationInfo;
final String codePath = pkg.codePath;
final File codeFile = new File(codePath);
@@ -7897,10 +8536,17 @@ public class PackageManagerService extends IPackageManager.Stub {
info.nativeLibraryRootRequiresIsa = false;
info.nativeLibraryDir = info.nativeLibraryRootDir;
} else {
- // Cluster install
- info.nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath();
+ if ((parseFlags & PackageParser.PARSE_IS_PREBUNDLED_DIR) != 0) {
+ // mAppLib32InstallDir is the directory /data/app-lib which is used to store native
+ // libs for apps from the system paritition. It isn't really specific to 32bit in
+ // any way except for the variable name, the system will use the primary/secondary
+ // ABI computed below.
+ info.nativeLibraryRootDir =
+ new File(mAppLib32InstallDir, pkg.packageName).getAbsolutePath();
+ } else {
+ info.nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath();
+ }
info.nativeLibraryRootRequiresIsa = true;
-
info.nativeLibraryDir = new File(info.nativeLibraryRootDir,
getPrimaryInstructionSet(info)).getAbsolutePath();
@@ -8617,7 +9263,6 @@ public class PackageManagerService extends IPackageManager.Stub {
& PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0) {
if (isSystemApp(pkg)) {
// For updated system applications, a system permission
- // is granted only if it had been defined by the original application.
if (pkg.isUpdatedSystemApp()) {
final PackageSetting sysPs = mSettings
.getDisabledSystemPkgLPr(pkg.packageName);
@@ -8685,6 +9330,9 @@ public class PackageManagerService extends IPackageManager.Stub {
allowed = origPermissions.hasInstallPermission(perm);
}
}
+ if (!allowed && bp.allowViaWhitelist) {
+ allowed = isAllowedSignature(pkg, perm);
+ }
return allowed;
}
@@ -8701,8 +9349,24 @@ public class PackageManagerService extends IPackageManager.Stub {
int userId) {
if (!sUserManager.exists(userId)) return null;
mFlags = flags;
- return super.queryIntent(intent, resolvedType,
+ List<ResolveInfo> list = super.queryIntent(intent, resolvedType,
(flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId);
+ // Remove protected Application components
+ int callingUid = Binder.getCallingUid();
+ String[] pkgs = getPackagesForUid(callingUid);
+ List<String> packages = (pkgs != null) ? Arrays.asList(pkgs) : Collections.EMPTY_LIST;
+ if (callingUid != Process.SYSTEM_UID &&
+ (getFlagsForUid(callingUid) & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ Iterator<ResolveInfo> itr = list.iterator();
+ while (itr.hasNext()) {
+ ActivityInfo activityInfo = itr.next().activityInfo;
+ if (activityInfo.applicationInfo.protect && (packages == null
+ || !packages.contains(activityInfo.packageName))) {
+ itr.remove();
+ }
+ }
+ }
+ return list;
}
public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
@@ -9382,8 +10046,8 @@ public class PackageManagerService extends IPackageManager.Stub {
};
final void sendPackageBroadcast(final String action, final String pkg,
- final Bundle extras, final String targetPkg, final IIntentReceiver finishedReceiver,
- final int[] userIds) {
+ final String intentCategory, final Bundle extras, final String targetPkg,
+ final IIntentReceiver finishedReceiver, final int[] userIds) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -9420,6 +10084,9 @@ public class PackageManagerService extends IPackageManager.Stub {
+ intent.toShortString(false, true, false, false)
+ " " + intent.getExtras(), here);
}
+ if (intentCategory != null) {
+ intent.addCategory(intentCategory);
+ }
am.broadcastIntent(null, intent, null, finishedReceiver,
0, null, null, null, android.app.AppOpsManager.OP_NONE,
null, finishedReceiver != null, false, id);
@@ -9586,7 +10253,7 @@ public class PackageManagerService extends IPackageManager.Stub {
Bundle extras = new Bundle(1);
extras.putInt(Intent.EXTRA_UID, UserHandle.getUid(userId, pkgSetting.appId));
- sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, null,
packageName, extras, null, null, new int[] {userId});
try {
IActivityManager am = ActivityManagerNative.getDefault();
@@ -10498,6 +11165,9 @@ public class PackageManagerService extends IPackageManager.Stub {
// reader
synchronized (mPackages) {
PackageParser.Package pkg = mPackages.get(packageName);
+ if (pkgLite.isTheme) {
+ return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ }
if (pkg != null) {
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
// Check for downgrading.
@@ -10637,7 +11307,7 @@ public class PackageManagerService extends IPackageManager.Stub {
loc = installLocationPolicy(pkgLite);
if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
- } else if (!onSd && !onInt) {
+ } else if ((!onSd && !onInt) || pkgLite.isTheme) {
// Override install location with flags
if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
// Set the flag to install on external media.
@@ -10649,6 +11319,9 @@ public class PackageManagerService extends IPackageManager.Stub {
installFlags |= PackageManager.INSTALL_INTERNAL;
installFlags &= ~PackageManager.INSTALL_EXTERNAL;
}
+ if (pkgLite.isTheme) {
+ installFlags &= ~PackageManager.INSTALL_FORWARD_LOCK;
+ }
}
}
}
@@ -12367,7 +13040,7 @@ public class PackageManagerService extends IPackageManager.Stub {
try {
derivePackageAbi(pkg, new File(pkg.codePath), args.abiOverride,
- true /* extract libs */);
+ true /* extract libs */, parseFlags);
} catch (PackageManagerException pme) {
Slog.e(TAG, "Error deriving application ABI", pme);
res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI");
@@ -12761,11 +13434,16 @@ public class PackageManagerService extends IPackageManager.Stub {
? info.removedAppId : info.uid);
extras.putBoolean(Intent.EXTRA_REPLACING, true);
- sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+ String category = null;
+ if (info.isThemeApk) {
+ category = Intent.CATEGORY_THEME_PACKAGE_INSTALLED_STATE_CHANGE;
+ }
+
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, category,
extras, null, null, null);
- sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, category,
extras, null, null, null);
- sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null,
+ sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null,
null, packageName, null, null);
}
}
@@ -12790,6 +13468,7 @@ public class PackageManagerService extends IPackageManager.Stub {
boolean isRemovedPackageSystemUpdate = false;
// Clean up resources deleted packages.
InstallArgs args = null;
+ boolean isThemeApk = false;
void sendBroadcast(boolean fullRemove, boolean replacing, boolean removedForAllUsers) {
Bundle extras = new Bundle(1);
@@ -12800,15 +13479,19 @@ public class PackageManagerService extends IPackageManager.Stub {
}
extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, removedForAllUsers);
if (removedPackage != null) {
- sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
+ String category = null;
+ if (isThemeApk) {
+ category = Intent.CATEGORY_THEME_PACKAGE_INSTALLED_STATE_CHANGE;
+ }
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, category,
extras, null, null, removedUsers);
if (fullRemove && !replacing) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED, removedPackage,
- extras, null, null, removedUsers);
+ category, extras, null, null, removedUsers);
}
}
if (removedAppId >= 0) {
- sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, null, null,
+ sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, null, extras, null, null,
removedUsers);
}
}
@@ -13115,7 +13798,8 @@ public class PackageManagerService extends IPackageManager.Stub {
false, //hidden
null, null, null,
false, // blockUninstall
- ps.readUserState(userId).domainVerificationStatus, 0);
+ ps.readUserState(userId).domainVerificationStatus, 0,
+ null, null);
if (!isSystemApp(ps)) {
if (ps.isAnyInstalled(sUserManager.getUserIds())) {
// Other user still have this package installed, so all
@@ -13188,6 +13872,15 @@ public class PackageManagerService extends IPackageManager.Stub {
outInfo, writeSettings);
}
+ //Cleanup theme related data
+ if (ps.pkg != null) {
+ if (ps.pkg.mOverlayTargets.size() > 0) {
+ uninstallThemeForAllApps(ps.pkg);
+ } else if (mOverlays.containsKey(ps.pkg.packageName)) {
+ uninstallThemeForApp(ps.pkg);
+ }
+ }
+
return ret;
}
@@ -14353,6 +15046,12 @@ public class PackageManagerService extends IPackageManager.Stub {
public void setComponentEnabledSetting(ComponentName componentName,
int newState, int flags, int userId) {
if (!sUserManager.exists(userId)) return;
+ // Don't allow to enable components marked for disabling at build-time
+ if (mDisabledComponentsList.contains(componentName)) {
+ Slog.d(TAG, "Ignoring attempt to set enabled state of disabled component "
+ + componentName.flattenToString());
+ return;
+ }
setEnabledSetting(componentName.getPackageName(),
componentName.getClassName(), newState, flags, userId, null);
}
@@ -14367,6 +15066,7 @@ public class PackageManagerService extends IPackageManager.Stub {
throw new IllegalArgumentException("Invalid new component state: "
+ newState);
}
+
PackageSetting pkgSetting;
final int uid = Binder.getCallingUid();
final int permission = mContext.checkCallingOrSelfPermission(
@@ -14494,7 +15194,7 @@ public class PackageManagerService extends IPackageManager.Stub {
extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList);
extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag);
extras.putInt(Intent.EXTRA_UID, packageUid);
- sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, null, null,
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, null, extras, null, null,
new int[] {UserHandle.getUserId(packageUid)});
}
@@ -15485,7 +16185,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
: Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
- sendPackageBroadcast(action, null, extras, null, finishedReceiver, null);
+ sendPackageBroadcast(action, null, null, extras, null, finishedReceiver, null);
}
}
@@ -16220,6 +16920,14 @@ public class PackageManagerService extends IPackageManager.Stub {
mSettings.createNewUserLILPw(this, mInstaller, userHandle);
applyFactoryDefaultBrowserLPw(userHandle);
primeDomainVerificationsLPw(userHandle);
+ // Set flag to monitor and not change apk file paths when
+ // scanning install directories.
+ final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING;
+ createAndSetCustomResources();
+ scanDirLI(Environment.getPrebundledDirectory(),
+ PackageParser.PARSE_IS_PREBUNDLED_DIR, scanFlags, 0,
+ new UserHandle(userHandle));
+ mCustomResources = null;
}
}
@@ -16275,6 +16983,69 @@ public class PackageManagerService extends IPackageManager.Stub {
}
@Override
+ public void setComponentProtectedSetting(ComponentName componentName, boolean newState,
+ int userId) {
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "set protected");
+
+ String packageName = componentName.getPackageName();
+ String className = componentName.getClassName();
+
+ PackageSetting pkgSetting;
+ ArrayList<String> components;
+
+ synchronized (mPackages) {
+ pkgSetting = mSettings.mPackages.get(packageName);
+
+ if (pkgSetting == null) {
+ if (className == null) {
+ throw new IllegalArgumentException(
+ "Unknown package: " + packageName);
+ }
+ throw new IllegalArgumentException(
+ "Unknown component: " + packageName
+ + "/" + className);
+ }
+
+ //Protection levels must be applied at the Component Level!
+ if (className == null) {
+ throw new IllegalArgumentException(
+ "Must specify Component Class name."
+ );
+ } else {
+ PackageParser.Package pkg = pkgSetting.pkg;
+ if (pkg == null || !pkg.hasComponentClassName(className)) {
+ if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) {
+ throw new IllegalArgumentException("Component class " + className
+ + " does not exist in " + packageName);
+ } else {
+ Slog.w(TAG, "Failed setComponentProtectedSetting: component class "
+ + className + " does not exist in " + packageName);
+ }
+ }
+
+ pkgSetting.protectComponentLPw(className, newState, userId);
+ mSettings.writePackageRestrictionsLPr(userId);
+
+ components = mPendingBroadcasts.get(userId, packageName);
+ final boolean newPackage = components == null;
+ if (newPackage) {
+ components = new ArrayList<String>();
+ }
+ if (!components.contains(className)) {
+ components.add(className);
+ }
+ }
+ }
+
+ long callingId = Binder.clearCallingIdentity();
+ try {
+ int packageUid = UserHandle.getUid(userId, pkgSetting.appId);
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
+ }
+
+ @Override
public boolean isStorageLow() {
final long token = Binder.clearCallingIdentity();
try {
@@ -16448,6 +17219,39 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+ @Override
+ public void updateIconMapping(String pkgName) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_CONFIGURATION,
+ "could not update icon mapping because caller "
+ + "does not have change config permission");
+
+ if (pkgName == null) {
+ clearIconMapping();
+ return;
+ }
+ mIconPackHelper = new IconPackHelper(mContext);
+ try {
+ mIconPackHelper.loadIconPack(pkgName);
+ } catch(NameNotFoundException e) {
+ Log.e(TAG, "Unable to find icon pack: " + pkgName);
+ clearIconMapping();
+ return;
+ }
+
+ for (Activity activity : mActivities.mActivities.values()) {
+ activity.info.themedIcon =
+ mIconPackHelper.getResourceIdForActivityIcon(activity.info);
+ }
+
+ synchronized (mPackages) {
+ for (Package pkg : mPackages.values()) {
+ pkg.applicationInfo.themedIcon =
+ mIconPackHelper.getResourceIdForApp(pkg.packageName);
+ }
+ }
+ }
+
private static class MoveCallbacks extends Handler {
private static final int MSG_CREATED = 1;
private static final int MSG_STATUS_CHANGED = 2;
@@ -16674,4 +17478,109 @@ public class PackageManagerService extends IPackageManager.Stub {
"Cannot call " + tag + " from UID " + callingUid);
}
}
+
+ private void clearIconMapping() {
+ mIconPackHelper = null;
+ for (Activity activity : mActivities.mActivities.values()) {
+ activity.info.themedIcon = 0;
+ }
+
+ for (Package pkg : mPackages.values()) {
+ pkg.applicationInfo.themedIcon = 0;
+ }
+ }
+
+ @Override
+ public ComposedIconInfo getComposedIconInfo() {
+ return mIconPackHelper != null ? mIconPackHelper.getComposedIconInfo() : null;
+ }
+
+ @Override
+ public int processThemeResources(String themePkgName) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.ACCESS_THEME_MANAGER, null);
+ PackageParser.Package pkg = mPackages.get(themePkgName);
+ if (pkg == null) {
+ Log.w(TAG, "Unable to get pkg for processing " + themePkgName);
+ return 0;
+ }
+
+ // Process icons
+ if (isIconCompileNeeded(pkg)) {
+ try {
+ ThemeUtils.createCacheDirIfNotExists();
+ ThemeUtils.createIconDirIfNotExists(pkg.packageName);
+ compileIconPack(pkg);
+ } catch (Exception e) {
+ uninstallThemeForAllApps(pkg);
+ deletePackageX(themePkgName, getCallingUid(), PackageManager.DELETE_ALL_USERS);
+ return PackageManager.INSTALL_FAILED_THEME_AAPT_ERROR;
+ }
+ }
+
+ int errorCode = 0;
+ // Generate Idmaps and res tables if pkg is a theme
+ for(String target : pkg.mOverlayTargets) {
+ Exception failedException = null;
+ try {
+ compileResourcesAndIdmapIfNeeded(mPackages.get(target), pkg);
+ } catch (IdmapException e) {
+ failedException = e;
+ errorCode = PackageManager.INSTALL_FAILED_THEME_IDMAP_ERROR;
+ } catch (AaptException e) {
+ failedException = e;
+ errorCode = PackageManager.INSTALL_FAILED_THEME_AAPT_ERROR;
+ } catch (Exception e) {
+ failedException = e;
+ errorCode = PackageManager.INSTALL_FAILED_THEME_UNKNOWN_ERROR;
+ }
+
+ if (failedException != null) {
+ Log.e(TAG, "Unable to process theme, uninstalling " + pkg.packageName,
+ failedException);
+ uninstallThemeForAllApps(pkg);
+ deletePackageX(themePkgName, getCallingUid(), PackageManager.DELETE_ALL_USERS);
+ return errorCode;
+ }
+ }
+
+ return 0;
+ }
+
+ private void processThemeResourcesInThemeService(String pkgName) {
+ ThemeManager tm =
+ (ThemeManager) mContext.getSystemService(Context.THEME_SERVICE);
+ if (tm != null) {
+ tm.processThemeResources(pkgName);
+ }
+ }
+
+ private void createAndSetCustomResources() {
+ Configuration tempConfiguration = new Configuration();
+ String mcc = SystemProperties.get("ro.prebundled.mcc");
+ if (!TextUtils.isEmpty(mcc)) {
+ tempConfiguration.mcc = Integer.parseInt(mcc);
+ mCustomResources = new Resources(new AssetManager(), new DisplayMetrics(),
+ tempConfiguration);
+ }
+ }
+
+ /**
+ * The new resource cache structure does not flatten the paths for idmaps, so this method
+ * checks for files that end with @idmap and assumes this indicates the older format and
+ * removes all files and directories from the resource cache so that it can be rebuilt
+ * using the new format.
+ */
+ private static void removeLegacyResourceCache() {
+ File cacheDir = new File(ThemeUtils.RESOURCE_CACHE_DIR);
+ if (cacheDir.exists()) {
+ for (File f : cacheDir.listFiles()) {
+ if (f.getName().endsWith(ThemeUtils.IDMAP_SUFFIX)) {
+ Log.i(TAG, "Removing old resource cache");
+ FileUtils.deleteContents(new File(ThemeUtils.RESOURCE_CACHE_DIR));
+ return;
+ }
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index bbdfe31..e02e4da 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -19,6 +19,7 @@ package com.android.server.pm;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.COMPONENT_VISIBLE_STATUS;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.PackageManager;
@@ -343,7 +344,8 @@ abstract class PackageSettingBase extends SettingBase {
boolean notLaunched, boolean hidden,
String lastDisableAppCaller, ArraySet<String> enabledComponents,
ArraySet<String> disabledComponents, boolean blockUninstall, int domainVerifState,
- int linkGeneration) {
+ int linkGeneration,
+ ArraySet<String> protectedComponents, ArraySet<String> visibleComponents) {
PackageUserState state = modifyUserState(userId);
state.enabled = enabled;
state.installed = installed;
@@ -356,6 +358,8 @@ abstract class PackageSettingBase extends SettingBase {
state.blockUninstall = blockUninstall;
state.domainVerificationStatus = domainVerifState;
state.appLinkGeneration = linkGeneration;
+ state.protectedComponents = protectedComponents;
+ state.visibleComponents = visibleComponents;
}
ArraySet<String> getEnabledComponents(int userId) {
@@ -395,6 +399,17 @@ abstract class PackageSettingBase extends SettingBase {
return state;
}
+ PackageUserState modifyUserStateComponents(int userId) {
+ PackageUserState state = modifyUserState(userId);
+ if (state.protectedComponents == null) {
+ state.protectedComponents = new ArraySet<String>(1);
+ }
+ if (state.visibleComponents == null) {
+ state.visibleComponents = new ArraySet<String>(1);
+ }
+ return state;
+ }
+
void addDisabledComponent(String componentClassName, int userId) {
modifyUserStateComponents(userId, true, false).disabledComponents.add(componentClassName);
}
@@ -440,6 +455,27 @@ abstract class PackageSettingBase extends SettingBase {
}
}
+ boolean protectComponentLPw(String componentClassName, boolean protect, int userId) {
+ PackageUserState state = modifyUserStateComponents(userId);
+ boolean changed = false;
+ if (protect == COMPONENT_VISIBLE_STATUS) {
+ changed = state.protectedComponents != null
+ ? state.protectedComponents.remove(componentClassName) : false;
+ changed |= state.visibleComponents.add(componentClassName);
+ } else {
+ changed = state.visibleComponents != null
+ ? state.visibleComponents.remove(componentClassName) : false;
+ changed |= state.protectedComponents.add(componentClassName);
+ }
+
+ return changed;
+ }
+
+ ArraySet<String> getProtectedComponents(int userId) {
+ PackageUserState state = modifyUserStateComponents(userId);
+ return state.protectedComponents;
+ }
+
void removeUser(int userId) {
userState.delete(userId);
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index a762014..f514e71 100644..100755
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -21,6 +21,7 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.INSTALL_FAILED_UNINSTALLED_PREBUNDLE;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
@@ -33,6 +34,8 @@ import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -56,6 +59,8 @@ import android.util.SparseLongArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.R;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.JournaledFile;
@@ -67,6 +72,7 @@ import com.android.server.pm.PermissionsState.PermissionState;
import java.io.FileNotFoundException;
import java.util.Collection;
+import java.util.HashSet;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -84,6 +90,7 @@ import android.content.pm.Signature;
import android.content.pm.UserInfo;
import android.content.pm.PackageUserState;
import android.content.pm.VerifierDeviceIdentity;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -93,9 +100,13 @@ import android.util.SparseIntArray;
import android.util.Xml;
import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
@@ -182,6 +193,9 @@ final class Settings {
private static final String TAG_DEFAULT_BROWSER = "default-browser";
private static final String TAG_VERSION = "version";
+ private static final String TAG_PROTECTED_COMPONENTS = "protected-components";
+ private static final String TAG_VISIBLE_COMPONENTS = "visible-components";
+
private static final String ATTR_NAME = "name";
private static final String ATTR_USER = "user";
private static final String ATTR_CODE = "code";
@@ -214,6 +228,7 @@ final class Settings {
private final File mPackageListFilename;
private final File mStoppedPackagesFilename;
private final File mBackupStoppedPackagesFilename;
+ private final File mPrebundledPackagesFilename;
final ArrayMap<String, PackageSetting> mPackages =
new ArrayMap<String, PackageSetting>();
@@ -281,6 +296,10 @@ final class Settings {
final SparseArray<PersistentPreferredIntentResolver> mPersistentPreferredActivities =
new SparseArray<PersistentPreferredIntentResolver>();
+ // The persistent prebundled packages for a user
+ final SparseArray<HashSet<String>> mPrebundledPackages =
+ new SparseArray<HashSet<String>>();
+
// For every user, it is used to find to which other users the intent can be forwarded.
final SparseArray<CrossProfileIntentResolver> mCrossProfileIntentResolvers =
new SparseArray<CrossProfileIntentResolver>();
@@ -359,6 +378,7 @@ final class Settings {
// Deprecated: Needed for migration
mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
+ mPrebundledPackagesFilename = new File(mSystemDir, "prebundled-packages.list");
}
PackageSetting getPackageLPw(PackageParser.Package pkg, PackageSetting origPackage,
@@ -652,7 +672,10 @@ final class Settings {
false, // hidden
null, null, null,
false, // blockUninstall
- INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0);
+ INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0,
+ null,
+ null
+ );
writePackageRestrictionsLPr(user.id);
}
}
@@ -1021,6 +1044,15 @@ final class Settings {
return pir;
}
+ HashSet<String> editPrebundledPackagesLPw(int userId) {
+ HashSet<String> hashSet = mPrebundledPackages.get(userId);
+ if (hashSet == null) {
+ hashSet = new HashSet<String>();
+ mPrebundledPackages.put(userId, hashSet);
+ }
+ return hashSet;
+ }
+
PersistentPreferredIntentResolver editPersistentPreferredActivitiesLPw(int userId) {
PersistentPreferredIntentResolver ppir = mPersistentPreferredActivities.get(userId);
if (ppir == null) {
@@ -1190,6 +1222,10 @@ final class Settings {
"package-restrictions-backup.xml");
}
+ private File getUserPrebundledStateFile(int userId) {
+ return new File(Environment.getUserSystemDirectory(userId), "prebundled-packages.list");
+ }
+
void writeAllUsersPackageRestrictionsLPr() {
List<UserInfo> users = getAllUsers();
if (users == null) return;
@@ -1414,7 +1450,10 @@ final class Settings {
false, // hidden
null, null, null,
false, // blockUninstall
- INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0);
+ INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0,
+ null,
+ null
+ );
}
return;
}
@@ -1499,6 +1538,8 @@ final class Settings {
ArraySet<String> enabledComponents = null;
ArraySet<String> disabledComponents = null;
+ ArraySet<String> protectedComponents = null;
+ ArraySet<String> visibleComponents = null;
int packageDepth = parser.getDepth();
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -1513,12 +1554,17 @@ final class Settings {
enabledComponents = readComponentsLPr(parser);
} else if (tagName.equals(TAG_DISABLED_COMPONENTS)) {
disabledComponents = readComponentsLPr(parser);
+ } else if (tagName.equals(TAG_PROTECTED_COMPONENTS)) {
+ protectedComponents = readComponentsLPr(parser);
+ } else if (tagName.equals(TAG_VISIBLE_COMPONENTS)) {
+ visibleComponents = readComponentsLPr(parser);
}
}
ps.setUserState(userId, enabled, installed, stopped, notLaunched, hidden,
enabledCaller, enabledComponents, disabledComponents, blockUninstall,
- verifState, linkGeneration);
+ verifState, linkGeneration,
+ protectedComponents, visibleComponents);
} else if (tagName.equals("preferred-activities")) {
readPreferredActivitiesLPw(parser, userId);
} else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {
@@ -1758,7 +1804,11 @@ final class Settings {
&& ustate.disabledComponents.size() > 0)
|| ustate.blockUninstall
|| (ustate.domainVerificationStatus !=
- PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED)) {
+ PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED)
+ || (ustate.protectedComponents != null
+ && ustate.protectedComponents.size() > 0)
+ || (ustate.visibleComponents != null
+ && ustate.visibleComponents.size() > 0)) {
serializer.startTag(null, TAG_PACKAGE);
serializer.attribute(null, ATTR_NAME, pkg.name);
if (DEBUG_MU) Log.i(TAG, " pkg=" + pkg.name + ", state=" + ustate.enabled);
@@ -1815,7 +1865,26 @@ final class Settings {
}
serializer.endTag(null, TAG_DISABLED_COMPONENTS);
}
-
+ if (ustate.protectedComponents != null
+ && ustate.protectedComponents.size() > 0) {
+ serializer.startTag(null, TAG_PROTECTED_COMPONENTS);
+ for (final String name : ustate.protectedComponents) {
+ serializer.startTag(null, TAG_ITEM);
+ serializer.attribute(null, ATTR_NAME, name);
+ serializer.endTag(null, TAG_ITEM);
+ }
+ serializer.endTag(null, TAG_PROTECTED_COMPONENTS);
+ }
+ if (ustate.visibleComponents != null
+ && ustate.visibleComponents.size() > 0) {
+ serializer.startTag(null, TAG_VISIBLE_COMPONENTS);
+ for (final String name : ustate.visibleComponents) {
+ serializer.startTag(null, TAG_ITEM);
+ serializer.attribute(null, ATTR_NAME, name);
+ serializer.endTag(null, TAG_ITEM);
+ }
+ serializer.endTag(null, TAG_VISIBLE_COMPONENTS);
+ }
serializer.endTag(null, TAG_PACKAGE);
}
}
@@ -2301,6 +2370,165 @@ final class Settings {
}
}
+ // Migrate from previous prebundled packages file to new one
+ void migratePrebundedPackagesIfNeededLPr(List<UserInfo> users, Installer installer) {
+ if (mPrebundledPackagesFilename.exists()) {
+ // Read old file
+ editPrebundledPackagesLPw(UserHandle.USER_OWNER);
+ readPrebundledPackagesOldLPw();
+ mPrebundledPackagesFilename.delete();
+ // Migrate to new file based on user
+ writePrebundledPackagesLPr(UserHandle.USER_OWNER);
+ } else {
+ if (users == null) {
+ readPrebundledPackagesLPr(UserHandle.USER_OWNER);
+ } else {
+ for (UserInfo user : users) {
+ editPrebundledPackagesLPw(user.id);
+ readPrebundledPackagesLPr(user.id);
+ // mark all existing users as having packages installed from OWNER
+ try {
+ markAllAsInstalledForUser(user.id, installer);
+ } catch (PackageManagerException e) {
+ Log.d(TAG, e.toString());
+ }
+ }
+ }
+ }
+ }
+
+ void writePrebundledPackagesLPr(int userId) {
+ editPrebundledPackagesLPw(userId);
+ PrintWriter writer = null;
+ try {
+ writer = new PrintWriter(
+ new BufferedWriter(new FileWriter(getUserPrebundledStateFile(userId), false)));
+
+ for (String packageName : mPrebundledPackages.get(userId)) {
+ writer.println(packageName);
+ }
+ } catch (IOException e) {
+ Slog.e(PackageManagerService.TAG, "Unable to write prebundled package list", e);
+ } finally {
+ if (writer != null) {
+ writer.close();
+ }
+ }
+ }
+
+ // This is done for an intermediate migration step on upgrade
+ void readPrebundledPackagesOldLPw() {
+ if (!mPrebundledPackagesFilename.exists()) {
+ return;
+ }
+
+ readPrebundledPackagesForUserFromFileLPr(UserHandle.USER_OWNER,
+ mPrebundledPackagesFilename);
+ }
+
+ void readPrebundledPackagesLPr(int userId) {
+ if (!getUserPrebundledStateFile(userId).exists()) {
+ return;
+ }
+ readPrebundledPackagesForUserFromFileLPr(userId, getUserPrebundledStateFile(userId));
+ }
+
+ private void readPrebundledPackagesForUserFromFileLPr(int userId, File file) {
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new FileReader(file));
+ String packageName = reader.readLine();
+ while (packageName != null) {
+ if (!TextUtils.isEmpty(packageName)) {
+ mPrebundledPackages.get(userId).add(packageName);
+ }
+ packageName = reader.readLine();
+ }
+ } catch (IOException e) {
+ Slog.e(PackageManagerService.TAG, "Unable to read prebundled package list", e);
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) {}
+ }
+ }
+ }
+
+ private void markAllAsInstalledForUser(int userHandle, Installer installer)
+ throws PackageManagerException {
+ if (mPrebundledPackages.get(userHandle) == null) {
+ throw new PackageManagerException(INSTALL_FAILED_UNINSTALLED_PREBUNDLE,
+ "Failure migrating prebundled packages to existing user " + userHandle);
+ }
+
+ // grab all the packages from the user account, move them over
+ for (String s : mPrebundledPackages.get(UserHandle.USER_OWNER)) {
+ mPrebundledPackages.get(userHandle).add(s);
+ }
+
+ for (PackageSetting ps : mPackages.values()) {
+ if (ps.pkg == null || ps.pkg.applicationInfo == null) {
+ continue;
+ }
+ // Mark the app as installed
+ boolean setInstalled =
+ wasPrebundledPackageInstalledLPr(UserHandle.USER_OWNER, ps.name);
+ ps.setInstalled(setInstalled, userHandle);
+ // Tell the installer to create the user data for the application
+ installer.createUserData(ps.name,
+ UserHandle.getUid(userHandle, ps.appId), userHandle,
+ ps.pkg.applicationInfo.seinfo);
+ }
+ // Write the package restrications
+ writePackageRestrictionsLPr(userHandle);
+ }
+
+ void markPrebundledPackageInstalledLPr(int userId, String packageName) {
+ editPrebundledPackagesLPw(userId);
+ mPrebundledPackages.get(userId).add(packageName);
+ }
+
+ boolean wasPrebundledPackageInstalledLPr(int userId, String packageName) {
+ if (mPrebundledPackages.get(userId) == null) {
+ return false;
+ }
+ return mPrebundledPackages.get(userId).contains(packageName);
+ }
+
+ boolean shouldPrebundledPackageBeInstalled(Resources res, String packageName,
+ Resources configuredResources) {
+ // Default fallback on lack of bad package
+ if (TextUtils.isEmpty(packageName)) {
+ return false;
+ }
+
+ // Configured resources can be null if the device
+ // is not region locked. In such cases, fall back to
+ // the default resources object
+ Resources resources = configuredResources;
+ if (configuredResources == null) {
+ resources = res;
+ }
+
+ // If the package is compatible with the current region, install it
+ // Note : If a package needs to be installed only if the device is
+ // not provisioned, overlay values/config_region_locked_packages
+ // TODO change config_region_locked_packages to something that is
+ // not confusing inside a non region resource bucket
+ String[] prebundledArray
+ = resources.getStringArray(R.array.config_region_locked_packages);
+ if (ArrayUtils.contains(prebundledArray, packageName)) {
+ return true;
+ }
+
+ // If the package is not compatible with the current region, check if its locked
+ // to any other region before installing it.
+ prebundledArray = resources
+ .getStringArray(R.array.config_restrict_to_region_locked_devices);
+ return !ArrayUtils.contains(prebundledArray, packageName);
+ }
+
void writeDisabledSysPackageLPr(XmlSerializer serializer, final PackageSetting pkg)
throws java.io.IOException {
serializer.startTag(null, "updated-package");
@@ -2459,6 +2687,9 @@ final class Settings {
}
}
}
+ if (bp.allowViaWhitelist) {
+ serializer.attribute(null, "allowViaWhitelist", Integer.toString(1));
+ }
serializer.endTag(null, TAG_ITEM);
}
}
@@ -2484,7 +2715,7 @@ final class Settings {
}
boolean readLPw(PackageManagerService service, List<UserInfo> users, int sdkVersion,
- boolean onlyCore) {
+ boolean onlyCore, Installer installer) {
FileInputStream str = null;
if (mBackupSettingsFilename.exists()) {
try {
@@ -2738,6 +2969,8 @@ final class Settings {
}
}
+ migratePrebundedPackagesIfNeededLPr(users, installer);
+
/*
* Make sure all the updated system packages have their shared users
* associated with them.
@@ -2959,6 +3192,11 @@ final class Settings {
for (int i=0; i<ri.size(); i++) {
ActivityInfo ai = ri.get(i).activityInfo;
set[i] = new ComponentName(ai.packageName, ai.name);
+ // We have already discovered the best third party match,
+ // so we only need to finish filling set with all results.
+ if (haveNonSys != null) {
+ continue;
+ }
if ((ai.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) {
if (ri.get(i).match >= thirdPartyMatch) {
// Keep track of the best match we find of all third
@@ -2967,7 +3205,6 @@ final class Settings {
if (PackageManagerService.DEBUG_PREFERRED) Log.d(TAG, "Result "
+ ai.packageName + "/" + ai.name + ": non-system!");
haveNonSys = set[i];
- break;
}
} else if (cn.getPackageName().equals(ai.packageName)
&& cn.getClassName().equals(ai.name)) {
@@ -3112,6 +3349,8 @@ final class Settings {
bp.protectionLevel = readInt(parser, null, "protection",
PermissionInfo.PROTECTION_NORMAL);
bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel);
+ bp.allowViaWhitelist = readInt(parser, null,
+ "allowViaWhitelist", 0) == 1;
if (dynamic) {
PermissionInfo pi = new PermissionInfo();
pi.packageName = sourcePackage.intern();
@@ -3119,6 +3358,7 @@ final class Settings {
pi.icon = readInt(parser, null, "icon", 0);
pi.nonLocalizedLabel = parser.getAttributeValue(null, "label");
pi.protectionLevel = bp.protectionLevel;
+ pi.allowViaWhitelist = bp.allowViaWhitelist;
bp.pendingInfo = pi;
}
out.put(bp.name, bp);
@@ -3655,7 +3895,9 @@ final class Settings {
continue;
}
// Only system apps are initially installed.
- ps.setInstalled((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0, userHandle);
+ boolean setInstalled = ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0) ||
+ wasPrebundledPackageInstalledLPr(UserHandle.USER_OWNER, ps.name);
+ ps.setInstalled(setInstalled, userHandle);
// Need to create a data directory for all apps under this user.
installer.createUserData(ps.volumeUuid, ps.name,
UserHandle.getUid(userHandle, ps.appId), userHandle,
@@ -3664,6 +3906,7 @@ final class Settings {
applyDefaultPreferredAppsLPw(service, userHandle);
writePackageRestrictionsLPr(userHandle);
writePackageListLPr(userHandle);
+ writePrebundledPackagesLPr(userHandle);
}
void removeUserLPw(int userId) {
@@ -3672,10 +3915,13 @@ final class Settings {
entry.getValue().removeUser(userId);
}
mPreferredActivities.remove(userId);
+ mPrebundledPackages.remove(userId);
File file = getUserPackagesStateFile(userId);
file.delete();
file = getUserPackagesStateBackupFile(userId);
file.delete();
+ file = getUserPrebundledStateFile(userId);
+ file.delete();
removeCrossProfileIntentFiltersLPw(userId);
mRuntimePermissionsPersistence.onUserRemoved(userId);
@@ -3850,7 +4096,7 @@ final class Settings {
if (pkgSetting.getNotLaunched(userId)) {
if (pkgSetting.installerPackageName != null) {
yucky.sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH,
- pkgSetting.name, null,
+ pkgSetting.name, null, null,
pkgSetting.installerPackageName, null, new int[] {userId});
}
pkgSetting.setNotLaunched(false, userId);
@@ -3860,6 +4106,57 @@ final class Settings {
return false;
}
+ void removeStalePermissions() {
+ /*
+ * Remove any permission that is not currently declared by any package
+ */
+ List<BasePermission> permissionsToRemove = new ArrayList<>();
+ for (BasePermission basePerm : mPermissions.values()) {
+ // Ignore permissions declared by the system
+ if (basePerm.sourcePackage.equals("android") ||
+ basePerm.sourcePackage.equals("cyanogenmod.platform")) {
+ continue;
+ }
+ // Ignore permissions other than NORMAL (ignore DYNAMIC and BUILTIN), like the
+ // ones based on permission-trees
+ if (basePerm.type != BasePermission.TYPE_NORMAL) {
+ continue;
+ }
+
+ if (!mPackages.containsKey(basePerm.sourcePackage)) {
+ // Package doesn't exist
+ permissionsToRemove.add(basePerm);
+ continue;
+ }
+ PackageSetting pkgSettings = mPackages.get(basePerm.sourcePackage);
+ if (pkgSettings.pkg == null || pkgSettings.pkg.permissions == null) {
+ // Package doesn't declare permissions
+ permissionsToRemove.add(basePerm);
+ continue;
+ }
+ boolean found = false;
+ for (PackageParser.Permission perm : pkgSettings.pkg.permissions) {
+ if (perm.info.name != null && basePerm.name.equals(perm.info.name)) {
+ // The original package still declares the permission
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ // The original package doesn't currently declare the permission
+ permissionsToRemove.add(basePerm);
+ }
+ }
+ // And now remove all stale permissions
+ for (BasePermission basePerm : permissionsToRemove) {
+ String msg = "Removed stale permission: " + basePerm.name + " originally " +
+ "assigned to " + basePerm.sourcePackage + "\n";
+ mReadMessages.append(msg);
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ mPermissions.remove(basePerm.name);
+ }
+ }
+
List<UserInfo> getAllUsers() {
long id = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/policy/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java
index 8e73495..9caa4c1 100644
--- a/services/core/java/com/android/server/policy/GlobalActions.java
+++ b/services/core/java/com/android/server/policy/GlobalActions.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2010-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.
@@ -28,20 +29,27 @@ import android.app.ActivityManagerNative;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.ContentResolver;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ThemeUtils;
import android.content.pm.UserInfo;
+import android.content.ServiceConnection;
import android.database.ContentObserver;
import android.graphics.drawable.Drawable;
+import android.Manifest;
import android.media.AudioManager;
import android.net.ConnectivityManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.IPowerManager;
import android.os.Message;
+import android.os.Messenger;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -79,6 +87,9 @@ import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
+import java.util.UUID;
+
+import static com.android.internal.util.cm.PowerMenuConstants.*;
/**
* Helper to show the global actions dialog. Each item is an {@link Action} that
@@ -91,21 +102,9 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
private static final boolean SHOW_SILENT_TOGGLE = true;
- /* Valid settings for global actions keys.
- * see config.xml config_globalActionList */
- private static final String GLOBAL_ACTION_KEY_POWER = "power";
- private static final String GLOBAL_ACTION_KEY_REBOOT = "reboot";
- private static final String GLOBAL_ACTION_KEY_AIRPLANE = "airplane";
- private static final String GLOBAL_ACTION_KEY_BUGREPORT = "bugreport";
- private static final String GLOBAL_ACTION_KEY_SILENT = "silent";
- private static final String GLOBAL_ACTION_KEY_USERS = "users";
- private static final String GLOBAL_ACTION_KEY_SETTINGS = "settings";
- private static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown";
- private static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist";
- private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
-
private final Context mContext;
private final WindowManagerFuncs mWindowManagerFuncs;
+ private Context mUiContext;
private final AudioManager mAudioManager;
private final IDreamManager mDreamManager;
@@ -125,6 +124,9 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
private boolean mHasVibrator;
private final boolean mShowSilentToggle;
+ // Power menu customizations
+ String mActions;
+
/**
* @param context everything needs a context :(
*/
@@ -139,6 +141,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(Intent.UPDATE_POWER_MENU);
filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
context.registerReceiver(mBroadcastReceiver, filter);
@@ -158,6 +161,8 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
mShowSilentToggle = SHOW_SILENT_TOGGLE && !mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useFixedVolume);
+
+ updatePowerMenuActions();
}
/**
@@ -167,12 +172,14 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
mKeyguardShowing = keyguardShowing;
mDeviceProvisioned = isDeviceProvisioned;
- if (mDialog != null) {
+ if (mDialog != null && mUiContext == null) {
mDialog.dismiss();
mDialog = null;
+ mDialog = createDialog();
// Show delayed, so that the dismiss of the previous dialog completes
mHandler.sendEmptyMessage(MESSAGE_SHOW);
} else {
+ mDialog = createDialog();
handleShow();
}
}
@@ -191,7 +198,6 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
private void handleShow() {
awakenIfNecessary();
- mDialog = createDialog();
prepareDialog();
// If we only have 1 item and it's a simple press action, just do this action.
@@ -208,6 +214,14 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
}
}
+ private Context getUiContext() {
+ if (mUiContext == null) {
+ mUiContext = ThemeUtils.createUiContext(mContext);
+ mUiContext.setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar);
+ }
+ return mUiContext != null ? mUiContext : mContext;
+ }
+
/**
* Create the global actions dialog.
* @return A new dialog.
@@ -263,20 +277,31 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
onAirplaneModeChanged();
mItems = new ArrayList<Action>();
- String[] defaultActions = mContext.getResources().getStringArray(
- com.android.internal.R.array.config_globalActionsList);
+
+ String[] actionsArray;
+ if (mActions == null) {
+ actionsArray = mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_globalActionsList);
+ } else {
+ actionsArray = mActions.split("\\|");
+ }
+
+ // Always add the power off option
+ mItems.add(new PowerAction());
ArraySet<String> addedKeys = new ArraySet<String>();
- for (int i = 0; i < defaultActions.length; i++) {
- String actionKey = defaultActions[i];
+ for (int i = 0; i < actionsArray.length; i++) {
+ String actionKey = actionsArray[i];
if (addedKeys.contains(actionKey)) {
// If we already have added this, don't add it again.
continue;
}
if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
- mItems.add(new PowerAction());
+ continue;
} else if (GLOBAL_ACTION_KEY_REBOOT.equals(actionKey)) {
mItems.add(new RebootAction());
+ } else if (GLOBAL_ACTION_KEY_SCREENSHOT.equals(actionKey)) {
+ mItems.add(getScreenshotAction());
} else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
mItems.add(mAirplaneModeOn);
} else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
@@ -289,7 +314,9 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
mItems.add(mSilentModeAction);
}
} else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
- if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
+ List<UserInfo> users = ((UserManager) mContext.getSystemService(
+ Context.USER_SERVICE)).getUsers();
+ if (users.size() > 1) {
addUsersToMenu(mItems);
}
} else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
@@ -309,12 +336,12 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
mAdapter = new MyAdapter();
- AlertParams params = new AlertParams(mContext);
+ AlertParams params = new AlertParams(getUiContext());
params.mAdapter = mAdapter;
params.mOnClickListener = this;
params.mForceInverseBackground = true;
- GlobalActionsDialog dialog = new GlobalActionsDialog(mContext, params);
+ GlobalActionsDialog dialog = new GlobalActionsDialog(getUiContext(), params);
dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
dialog.getListView().setItemsCanFocus(true);
@@ -400,6 +427,24 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
}
}
+ private Action getScreenshotAction() {
+ return new SinglePressAction(com.android.internal.R.drawable.ic_lock_screenshot,
+ R.string.global_action_screenshot) {
+
+ public void onPress() {
+ takeScreenshot();
+ }
+
+ public boolean showDuringKeyguard() {
+ return true;
+ }
+
+ public boolean showBeforeProvisioning() {
+ return true;
+ }
+ };
+ }
+
private Action getBugReportAction() {
return new SinglePressAction(com.android.internal.R.drawable.ic_lock_bugreport,
R.string.bugreport_title) {
@@ -456,7 +501,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
}
private Action getSettingsAction() {
- return new SinglePressAction(com.android.internal.R.drawable.ic_settings,
+ return new SinglePressAction(com.android.internal.R.drawable.ic_lock_settings,
R.string.global_action_settings) {
@Override
@@ -573,7 +618,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
Drawable icon = user.iconPath != null ? Drawable.createFromPath(user.iconPath)
: null;
SinglePressAction switchToUser = new SinglePressAction(
- com.android.internal.R.drawable.ic_menu_cc, icon,
+ com.android.internal.R.drawable.ic_lock_user, icon,
(user.name != null ? user.name : "Primary")
+ (isCurrentUser ? " \u2714" : "")) {
public void onPress() {
@@ -598,6 +643,90 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
}
}
+ /**
+ * functions needed for taking screenhots.
+ * This leverages the built in ICS screenshot functionality
+ */
+ final Object mScreenshotLock = new Object();
+ ServiceConnection mScreenshotConnection = null;
+
+ final Runnable mScreenshotTimeout = new Runnable() {
+ @Override public void run() {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != null) {
+ mContext.unbindService(mScreenshotConnection);
+ mScreenshotConnection = null;
+ }
+ }
+ }
+ };
+
+ private void takeScreenshot() {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != null) {
+ return;
+ }
+ ComponentName cn = new ComponentName("com.android.systemui",
+ "com.android.systemui.screenshot.TakeScreenshotService");
+ Intent intent = new Intent();
+ intent.setComponent(cn);
+ ServiceConnection conn = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != this) {
+ return;
+ }
+ Messenger messenger = new Messenger(service);
+ Message msg = Message.obtain(null, 1);
+ final ServiceConnection myConn = this;
+ Handler h = new Handler(mHandler.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection == myConn) {
+ mContext.unbindService(mScreenshotConnection);
+ mScreenshotConnection = null;
+ mHandler.removeCallbacks(mScreenshotTimeout);
+ }
+ }
+ }
+ };
+ msg.replyTo = new Messenger(h);
+ msg.arg1 = msg.arg2 = 0;
+
+ /* remove for the time being
+ if (mStatusBar != null && mStatusBar.isVisibleLw())
+ msg.arg1 = 1;
+ if (mNavigationBar != null && mNavigationBar.isVisibleLw())
+ msg.arg2 = 1;
+ */
+
+ /* wait for the dialog box to close */
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException ie) {
+ // Do nothing
+ }
+
+ /* take the screenshot */
+ try {
+ messenger.send(msg);
+ } catch (RemoteException e) {
+ // Do nothing
+ }
+ }
+ }
+ @Override
+ public void onServiceDisconnected(ComponentName name) {}
+ };
+ if (mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE)) {
+ mScreenshotConnection = conn;
+ mHandler.postDelayed(mScreenshotTimeout, 10000);
+ }
+ }
+ }
+
private void prepareDialog() {
refreshSilentMode();
mAirplaneModeOn.updateState(mAirplaneState);
@@ -704,7 +833,8 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
public View getView(int position, View convertView, ViewGroup parent) {
Action action = getItem(position);
- return action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
+ final Context context = getUiContext();
+ return action.create(context, convertView, parent, LayoutInflater.from(context));
}
}
@@ -1073,10 +1203,24 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
mIsWaitingForEcmExit = false;
changeAirplaneModeSystemSetting(true);
}
+ } else if (Intent.UPDATE_POWER_MENU.equals(action)) {
+ updatePowerMenuActions();
}
}
};
+ protected void updatePowerMenuActions() {
+ ContentResolver resolver = mContext.getContentResolver();
+ mActions = Settings.Secure.getStringForUser(resolver,
+ Settings.Secure.POWER_MENU_ACTIONS, UserHandle.USER_CURRENT);
+ }
+
+ private BroadcastReceiver mThemeChangeReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ mUiContext = null;
+ }
+ };
+
PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
@Override
public void onServiceStateChanged(ServiceState serviceState) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 899c176..ca586e8 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -31,6 +31,7 @@ import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
@@ -47,6 +48,7 @@ import android.graphics.Rect;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPlaybackClient;
import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
+import android.hardware.input.InputManager;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioSystem;
@@ -80,6 +82,11 @@ import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
import android.speech.RecognizerIntent;
import android.telecom.TelecomManager;
+import android.service.gesture.EdgeGestureManager;
+import com.android.internal.os.DeviceKeyHandler;
+
+import com.android.internal.util.cm.ActionUtils;
+import dalvik.system.DexClassLoader;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
@@ -113,9 +120,15 @@ import android.view.accessibility.AccessibilityManager;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
+import android.widget.Toast;
+
import com.android.internal.R;
+import com.android.internal.policy.IKeyguardService;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.ScreenShapeHelper;
+import com.android.internal.util.gesture.EdgeGesturePosition;
+import com.android.internal.util.gesture.EdgeServiceConstants;
+import com.android.internal.view.RotationPolicy;
import com.android.internal.widget.PointerLocationView;
import com.android.server.LocalServices;
import com.android.server.policy.keyguard.KeyguardServiceDelegate;
@@ -128,6 +141,7 @@ import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.lang.reflect.Constructor;
import static android.view.WindowManager.LayoutParams.*;
import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
@@ -200,6 +214,30 @@ public class PhoneWindowManager implements WindowManagerPolicy {
static public final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
static public final String SYSTEM_DIALOG_REASON_ASSIST = "assist";
+ // Available custom actions to perform on a key press.
+ // Must match values for KEY_HOME_LONG_PRESS_ACTION in:
+ // core/java/android/provider/Settings.java
+ private static final int KEY_ACTION_NOTHING = 0;
+ private static final int KEY_ACTION_MENU = 1;
+ private static final int KEY_ACTION_APP_SWITCH = 2;
+ private static final int KEY_ACTION_SEARCH = 3;
+ private static final int KEY_ACTION_VOICE_SEARCH = 4;
+ private static final int KEY_ACTION_IN_APP_SEARCH = 5;
+ private static final int KEY_ACTION_LAUNCH_CAMERA = 6;
+ private static final int KEY_ACTION_SLEEP = 7;
+ private static final int KEY_ACTION_LAST_APP = 8;
+
+ // Masks for checking presence of hardware keys.
+ // Must match values in core/res/res/values/config.xml
+ private static final int KEY_MASK_HOME = 0x01;
+ private static final int KEY_MASK_BACK = 0x02;
+ private static final int KEY_MASK_MENU = 0x04;
+ private static final int KEY_MASK_ASSIST = 0x08;
+ private static final int KEY_MASK_APP_SWITCH = 0x10;
+ private static final int KEY_MASK_CAMERA = 0x20;
+ private static final int KEY_MASK_VOLUME = 0x40;
+
+
/**
* These are the system UI flags that, when changing, can cause the layout
* of the screen to change.
@@ -266,6 +304,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
/** Amount of time (in milliseconds) to wait for windows drawn before powering on. */
static final int WAITING_FOR_DRAWN_TIMEOUT = 1000;
+ private DeviceKeyHandler mDeviceKeyHandler;
+
/**
* Lock protecting internal state. Must not call out into window
* manager with lock held. (This lock will be acquired in places
@@ -324,10 +364,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
boolean mCanHideNavigationBar = false;
boolean mNavigationBarCanMove = false; // can the navigation bar ever move to the side?
boolean mNavigationBarOnBottom = true; // is the navigation bar on the bottom *right now*?
+ boolean mNavigationBarLeftInLandscape = false; // Navigation bar left handed?
int[] mNavigationBarHeightForRotation = new int[4];
int[] mNavigationBarWidthForRotation = new int[4];
- boolean mBootMessageNeedsHiding;
KeyguardServiceDelegate mKeyguardDelegate;
final Runnable mWindowManagerDrawCallback = new Runnable() {
@Override
@@ -341,6 +381,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
public void onDrawn() {
if (DEBUG_WAKEUP) Slog.d(TAG, "mKeyguardDelegate.ShowListener.onDrawn.");
mHandler.sendEmptyMessage(MSG_KEYGUARD_DRAWN_COMPLETE);
+ hideBootMessages();
}
};
@@ -378,6 +419,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
int mUiMode;
int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED;
int mLidOpenRotation;
+ boolean mHasRemovableLid;
int mCarDockRotation;
int mDeskDockRotation;
int mUndockedHdmiRotation;
@@ -395,6 +437,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
int mUserRotation = Surface.ROTATION_0;
+ int mUserRotationAngles = -1;
boolean mAccelerometerDefault;
boolean mSupportAutoRotation;
@@ -420,6 +463,28 @@ public class PhoneWindowManager implements WindowManagerPolicy {
boolean mHasSoftInput = false;
boolean mTranslucentDecorEnabled = true;
boolean mUseTvRouting;
+ int mBackKillTimeout;
+
+ int mDeviceHardwareKeys;
+
+ // Button wake control flags
+ boolean mHomeWakeScreen;
+ boolean mBackWakeScreen;
+ boolean mMenuWakeScreen;
+ boolean mAssistWakeScreen;
+ boolean mAppSwitchWakeScreen;
+ boolean mCameraWakeScreen;
+ boolean mVolumeWakeScreen;
+
+ // Camera button control flags and actions
+ boolean mCameraSleepOnRelease;
+ boolean mIsFocusPressed;
+ boolean mCameraLaunch;
+
+ // During wakeup by volume keys, we still need to capture subsequent events
+ // until the key is released. This is required since the beep sound is produced
+ // post keypressed.
+ boolean mVolumeWakeTriggered;
int mPointerLocationMode = 0; // guarded by mLock
@@ -427,6 +492,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
WindowState mFocusedWindow;
IApplicationToken mFocusedApp;
+ // Behavior of volbtn music controls
+ boolean mVolBtnMusicControls;
+ boolean mIsLongPress;
+
PointerLocationView mPointerLocationView;
// The current size of the screen; really; extends into the overscan area of
@@ -511,6 +580,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
boolean mForcingShowNavBar;
int mForcingShowNavBarLayer;
+ boolean mDevForceNavbar = false;
+
// States of keyguard dismiss.
private static final int DISMISS_KEYGUARD_NONE = 0; // Keyguard not being dismissed.
private static final int DISMISS_KEYGUARD_START = 1; // Keyguard needs to be dismissed.
@@ -545,6 +616,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
boolean mHomePressed;
boolean mHomeConsumed;
boolean mHomeDoubleTapPending;
+ boolean mMenuPressed;
+ boolean mAppSwitchLongPressed;
Intent mHomeIntent;
Intent mCarDockIntent;
Intent mDeskDockIntent;
@@ -553,6 +626,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
boolean mAssistKeyLongPressed;
boolean mPendingMetaAction;
+ // Tracks user-customisable behavior for certain key events
+ private int mLongPressOnHomeBehavior = -1;
+ private int mPressOnMenuBehavior = -1;
+ private int mLongPressOnMenuBehavior = -1;
+ private int mPressOnAssistBehavior = -1;
+ private int mLongPressOnAssistBehavior = -1;
+ private int mPressOnAppSwitchBehavior = -1;
+ private int mLongPressOnAppSwitchBehavior = -1;
+
// support for activating the lock screen while the screen is on
boolean mAllowLockscreenWhenOn;
int mLockScreenTimeout;
@@ -579,9 +661,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
int mOverscanRight = 0;
int mOverscanBottom = 0;
- // What we do when the user long presses on home
- private int mLongPressOnHomeBehavior;
-
// What we do when the user double-taps on home
private int mDoubleTapOnHomeBehavior;
@@ -648,9 +727,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private static final int MSG_POWER_DELAYED_PRESS = 13;
private static final int MSG_POWER_LONG_PRESS = 14;
private static final int MSG_UPDATE_DREAMING_SLEEP_TOKEN = 15;
+ private static final int MSG_DISPATCH_VOLKEY_WITH_WAKE_LOCK = 16;
+ private static final int MSG_CAMERA_LONG_PRESS = 17;
+
boolean mWifiDisplayConnected = false;
int mWifiDisplayCustomRotation = -1;
+ private boolean mHasPermanentMenuKey;
+
private class PolicyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
@@ -701,6 +785,19 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case MSG_UPDATE_DREAMING_SLEEP_TOKEN:
updateDreamingSleepToken(msg.arg1 != 0);
break;
+ case MSG_DISPATCH_VOLKEY_WITH_WAKE_LOCK: {
+ KeyEvent event = (KeyEvent) msg.obj;
+ mIsLongPress = true;
+ dispatchMediaKeyWithWakeLockToAudioService(event);
+ dispatchMediaKeyWithWakeLockToAudioService(
+ KeyEvent.changeAction(event, KeyEvent.ACTION_UP));
+ break;
+ }
+ case MSG_CAMERA_LONG_PRESS: {
+ KeyEvent event = (KeyEvent) msg.obj;
+ mIsLongPress = true;
+ break;
+ }
}
}
}
@@ -741,6 +838,30 @@ public class PhoneWindowManager implements WindowManagerPolicy {
resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.POINTER_LOCATION), false, this,
UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.KEY_HOME_LONG_PRESS_ACTION), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.KEY_HOME_DOUBLE_TAP_ACTION), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.KEY_MENU_ACTION), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.KEY_MENU_LONG_PRESS_ACTION), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.KEY_ASSIST_ACTION), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.KEY_ASSIST_LONG_PRESS_ACTION), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.KEY_APP_SWITCH_ACTION), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.KEY_APP_SWITCH_LONG_PRESS_ACTION), false, this,
+ UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.DEFAULT_INPUT_METHOD), false, this,
UserHandle.USER_ALL);
@@ -750,6 +871,54 @@ public class PhoneWindowManager implements WindowManagerPolicy {
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.POLICY_CONTROL), false, this,
UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.DEV_FORCE_SHOW_NAVBAR), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.ACCELEROMETER_ROTATION_ANGLES), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.DEV_FORCE_SHOW_NAVBAR), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.VOLBTN_MUSIC_CONTROLS), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.USE_EDGE_SERVICE_FOR_GESTURES), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.BACK_WAKE_SCREEN), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.MENU_WAKE_SCREEN), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.ASSIST_WAKE_SCREEN), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.APP_SWITCH_WAKE_SCREEN), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.CAMERA_WAKE_SCREEN), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.CAMERA_SLEEP_ON_RELEASE), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.CAMERA_LAUNCH), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.VOLUME_WAKE_SCREEN), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.HOME_WAKE_SCREEN), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.VOLUME_WAKE_SCREEN), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.NAVBAR_LEFT_IN_LANDSCAPE), false, this,
+ UserHandle.USER_ALL);
updateSettings();
}
@@ -802,6 +971,67 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private SystemGesturesPointerEventListener mSystemGestures;
+ private EdgeGestureManager.EdgeGestureActivationListener mEdgeGestureActivationListener
+ = new EdgeGestureManager.EdgeGestureActivationListener() {
+
+ @Override
+ public void onEdgeGestureActivation(int touchX, int touchY,
+ EdgeGesturePosition position, int flags) {
+ WindowState target = null;
+
+ if (position == EdgeGesturePosition.TOP) {
+ target = mStatusBar;
+ } else if (position == EdgeGesturePosition.BOTTOM && mNavigationBarOnBottom) {
+ target = mNavigationBar;
+ } else if (position == EdgeGesturePosition.LEFT
+ && !mNavigationBarOnBottom && mNavigationBarLeftInLandscape) {
+ target = mNavigationBar;
+ } else if (position == EdgeGesturePosition.RIGHT && !mNavigationBarOnBottom) {
+ target = mNavigationBar;
+ }
+
+ if (target != null) {
+ requestTransientBars(target);
+ dropEventsUntilLift();
+ mEdgeListenerActivated = true;
+ } else {
+ restoreListenerState();
+ }
+ }
+ };
+ private EdgeGestureManager mEdgeGestureManager = null;
+ private int mLastEdgePositions = 0;
+ private boolean mEdgeListenerActivated = false;
+ private boolean mUsingEdgeGestureServiceForGestures = false;
+
+ private void updateEdgeGestureListenerState() {
+ int flags = 0;
+ if (mUsingEdgeGestureServiceForGestures) {
+ flags = EdgeServiceConstants.LONG_LIVING | EdgeServiceConstants.UNRESTRICTED;
+ if (mStatusBar != null && !mStatusBar.isVisibleLw()) {
+ flags |= EdgeGesturePosition.TOP.FLAG;
+ }
+ if (mNavigationBar != null && !mNavigationBar.isVisibleLw() && !isStatusBarKeyguard()) {
+ if (mNavigationBarOnBottom) {
+ flags |= EdgeGesturePosition.BOTTOM.FLAG;
+ } else if (mNavigationBarLeftInLandscape) {
+ flags |= EdgeGesturePosition.LEFT.FLAG;
+ } else {
+ flags |= EdgeGesturePosition.RIGHT.FLAG;
+ }
+ }
+ }
+ if (mEdgeListenerActivated) {
+ mEdgeGestureActivationListener.restoreListenerState();
+ mEdgeListenerActivated = false;
+ }
+ if (flags != mLastEdgePositions) {
+ mEdgeGestureManager.updateEdgeGestureActivationListener(mEdgeGestureActivationListener,
+ flags);
+ mLastEdgePositions = flags;
+ }
+ }
+
IStatusBarService getStatusBarService() {
synchronized (mServiceAquireLock) {
if (mStatusBarService == null) {
@@ -910,7 +1140,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// taken over the whole screen.
boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(interactive,
SystemClock.elapsedRealtime(), isImmersiveMode(mLastSystemUiFlags));
- if (panic) {
+ if (panic && !PolicyControl.isImmersiveFiltersActive()) {
mHandler.post(mHiddenNavPanic);
}
@@ -1208,6 +1438,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mHandler.sendEmptyMessage(MSG_DISPATCH_SHOW_GLOBAL_ACTIONS);
}
+ Runnable mBackLongPress = new Runnable() {
+ public void run() {
+ if (ActionUtils.killForegroundApp(mContext, mCurrentUserId)) {
+ performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+ Toast.makeText(mContext, R.string.app_killed_message, Toast.LENGTH_SHORT).show();
+ }
+ }
+ };
+
void showGlobalActionsInternal() {
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
if (mGlobalActions == null) {
@@ -1287,26 +1526,57 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
- private void handleLongPressOnHome(int deviceId) {
- if (mLongPressOnHomeBehavior != LONG_PRESS_HOME_NOTHING) {
- mHomeConsumed = true;
- performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+ private void triggerVirtualKeypress(final int keyCode) {
+ InputManager im = InputManager.getInstance();
+ long now = SystemClock.uptimeMillis();
+ final KeyEvent downEvent = new KeyEvent(now, now, KeyEvent.ACTION_DOWN,
+ keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
+ KeyEvent.FLAG_FROM_SYSTEM, InputDevice.SOURCE_KEYBOARD);
+ final KeyEvent upEvent = KeyEvent.changeAction(downEvent, KeyEvent.ACTION_UP);
- if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_SYSTEM_UI) {
- toggleRecentApps();
- } else if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_ASSIST) {
- launchAssistAction(null, deviceId);
- }
- }
+ im.injectInputEvent(downEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+ im.injectInputEvent(upEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}
- private void handleDoubleTapOnHome() {
- if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) {
- mHomeConsumed = true;
- toggleRecentApps();
- }
+ private void launchCameraAction() {
+ sendCloseSystemWindows();
+ Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
+ startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
}
+ private void performKeyAction(int behavior, KeyEvent event) {
+ switch (behavior) {
+ case KEY_ACTION_NOTHING:
+ break;
+ case KEY_ACTION_MENU:
+ triggerVirtualKeypress(KeyEvent.KEYCODE_MENU);
+ break;
+ case KEY_ACTION_APP_SWITCH:
+ toggleRecentApps();
+ break;
+ case KEY_ACTION_SEARCH:
+ launchAssistAction(null, event.getDeviceId());
+ break;
+ case KEY_ACTION_VOICE_SEARCH:
+ launchAssistLongPressAction();
+ break;
+ case KEY_ACTION_IN_APP_SEARCH:
+ triggerVirtualKeypress(KeyEvent.KEYCODE_SEARCH);
+ break;
+ case KEY_ACTION_LAUNCH_CAMERA:
+ launchCameraAction();
+ break;
+ case KEY_ACTION_SLEEP:
+ mPowerManager.goToSleep(SystemClock.uptimeMillis());
+ break;
+ case KEY_ACTION_LAST_APP:
+ ActionUtils.switchToLastApp(mContext, mCurrentUserId);
+ break;
+ default:
+ break;
+ }
+ }
+
private final Runnable mHomeDoubleTapTimeoutRunnable = new Runnable() {
@Override
public void run() {
@@ -1456,7 +1726,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mUseTvRouting = AudioSystem.getPlatformType(mContext) == AudioSystem.PLATFORM_TELEVISION;
- readConfigurationDependentBehaviors();
+ mDeviceHardwareKeys = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_deviceHardwareKeys);
+ mHasRemovableLid = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_hasRemovableLid);
+ mBackKillTimeout = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_backKillTimeout);
+
+ updateKeyAssignments();
mAccessibilityManager = (AccessibilityManager) context.getSystemService(
Context.ACCESSIBILITY_SERVICE);
@@ -1502,7 +1779,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
public void onSwipeFromRight() {
- if (mNavigationBar != null && !mNavigationBarOnBottom) {
+ if (mNavigationBar != null && !mNavigationBarOnBottom &&
+ !mNavigationBarLeftInLandscape) {
+ requestTransientBars(mNavigationBar);
+ }
+ }
+ @Override
+ public void onSwipeFromLeft() {
+ if (mNavigationBar != null && !mNavigationBarOnBottom &&
+ mNavigationBarLeftInLandscape) {
requestTransientBars(mNavigationBar);
}
}
@@ -1561,27 +1846,125 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mWindowManagerInternal.registerAppTransitionListener(
mStatusBarController.getAppTransitionListener());
+
+ String deviceKeyHandlerLib = mContext.getResources().getString(
+ com.android.internal.R.string.config_deviceKeyHandlerLib);
+
+ String deviceKeyHandlerClass = mContext.getResources().getString(
+ com.android.internal.R.string.config_deviceKeyHandlerClass);
+
+ if (!deviceKeyHandlerLib.isEmpty() && !deviceKeyHandlerClass.isEmpty()) {
+ DexClassLoader loader = new DexClassLoader(deviceKeyHandlerLib,
+ new ContextWrapper(mContext).getCacheDir().getAbsolutePath(),
+ null,
+ ClassLoader.getSystemClassLoader());
+ try {
+ Class<?> klass = loader.loadClass(deviceKeyHandlerClass);
+ Constructor<?> constructor = klass.getConstructor(Context.class);
+ mDeviceKeyHandler = (DeviceKeyHandler) constructor.newInstance(
+ mContext);
+ if(DEBUG) Slog.d(TAG, "Device key handler loaded");
+ } catch (Exception e) {
+ Slog.w(TAG, "Could not instantiate device key handler "
+ + deviceKeyHandlerClass + " from class "
+ + deviceKeyHandlerLib, e);
+ }
+ }
+
}
- /**
- * Read values from config.xml that may be overridden depending on
- * the configuration of the device.
- * eg. Disable long press on home goes to recents on sw600dp.
- */
- private void readConfigurationDependentBehaviors() {
+ private void updateKeyAssignments() {
+ int activeHardwareKeys = mDeviceHardwareKeys;
+
+ if (mDevForceNavbar) {
+ activeHardwareKeys = 0;
+ }
+ final boolean hasMenu = (activeHardwareKeys & KEY_MASK_MENU) != 0;
+ final boolean hasHome = (activeHardwareKeys & KEY_MASK_HOME) != 0;
+ final boolean hasAssist = (activeHardwareKeys & KEY_MASK_ASSIST) != 0;
+ final boolean hasAppSwitch = (activeHardwareKeys & KEY_MASK_APP_SWITCH) != 0;
+
+ final ContentResolver resolver = mContext.getContentResolver();
+
+ // Initialize all assignments to sane defaults.
+ mPressOnMenuBehavior = KEY_ACTION_MENU;
+
+ mLongPressOnMenuBehavior = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_longPressOnMenuBehavior);
+
+ if (mLongPressOnMenuBehavior == KEY_ACTION_NOTHING &&
+ (hasMenu && !hasAssist)) {
+ mLongPressOnMenuBehavior = KEY_ACTION_SEARCH;
+ }
+ mPressOnAssistBehavior = KEY_ACTION_SEARCH;
+ mLongPressOnAssistBehavior = KEY_ACTION_VOICE_SEARCH;
+ mPressOnAppSwitchBehavior = KEY_ACTION_APP_SWITCH;
+ mLongPressOnAppSwitchBehavior = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_longPressOnAppSwitchBehavior);
+
mLongPressOnHomeBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnHomeBehavior);
- if (mLongPressOnHomeBehavior < LONG_PRESS_HOME_NOTHING ||
- mLongPressOnHomeBehavior > LONG_PRESS_HOME_ASSIST) {
- mLongPressOnHomeBehavior = LONG_PRESS_HOME_NOTHING;
+ if (mLongPressOnHomeBehavior < KEY_ACTION_NOTHING ||
+ mLongPressOnHomeBehavior > KEY_ACTION_SLEEP) {
+ mLongPressOnHomeBehavior = KEY_ACTION_NOTHING;
}
mDoubleTapOnHomeBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_doubleTapOnHomeBehavior);
- if (mDoubleTapOnHomeBehavior < DOUBLE_TAP_HOME_NOTHING ||
- mDoubleTapOnHomeBehavior > DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) {
- mDoubleTapOnHomeBehavior = LONG_PRESS_HOME_NOTHING;
+ if (mDoubleTapOnHomeBehavior < KEY_ACTION_NOTHING ||
+ mDoubleTapOnHomeBehavior > KEY_ACTION_SLEEP) {
+ mDoubleTapOnHomeBehavior = KEY_ACTION_NOTHING;
+ }
+
+ boolean hasPermanentMenu = false;
+
+ // Check for custom assignments and whether KEY_ACTION_MENU is assigned.
+ if (hasHome) {
+ mLongPressOnHomeBehavior = Settings.System.getIntForUser(resolver,
+ Settings.System.KEY_HOME_LONG_PRESS_ACTION,
+ mLongPressOnHomeBehavior, UserHandle.USER_CURRENT);
+ mDoubleTapOnHomeBehavior = Settings.System.getIntForUser(resolver,
+ Settings.System.KEY_HOME_DOUBLE_TAP_ACTION,
+ mDoubleTapOnHomeBehavior, UserHandle.USER_CURRENT);
+
+ hasPermanentMenu = mLongPressOnHomeBehavior == KEY_ACTION_MENU
+ || mDoubleTapOnHomeBehavior == KEY_ACTION_MENU;
+ }
+ if (hasMenu) {
+ mPressOnMenuBehavior = Settings.System.getIntForUser(resolver,
+ Settings.System.KEY_MENU_ACTION,
+ mPressOnMenuBehavior, UserHandle.USER_CURRENT);
+ mLongPressOnMenuBehavior = Settings.System.getIntForUser(resolver,
+ Settings.System.KEY_MENU_LONG_PRESS_ACTION,
+ mLongPressOnMenuBehavior, UserHandle.USER_CURRENT);
+
+ hasPermanentMenu |= mPressOnMenuBehavior == KEY_ACTION_MENU
+ || mLongPressOnMenuBehavior == KEY_ACTION_MENU;
+ }
+ if (hasAssist) {
+ mPressOnAssistBehavior = Settings.System.getIntForUser(resolver,
+ Settings.System.KEY_ASSIST_ACTION,
+ mPressOnAssistBehavior, UserHandle.USER_CURRENT);
+ mLongPressOnAssistBehavior = Settings.System.getIntForUser(resolver,
+ Settings.System.KEY_ASSIST_LONG_PRESS_ACTION,
+ mLongPressOnAssistBehavior, UserHandle.USER_CURRENT);
+
+ hasPermanentMenu |= mPressOnAssistBehavior == KEY_ACTION_MENU
+ || mLongPressOnAssistBehavior == KEY_ACTION_MENU;
+ }
+ if (hasAppSwitch) {
+ mPressOnAppSwitchBehavior = Settings.System.getIntForUser(resolver,
+ Settings.System.KEY_APP_SWITCH_ACTION,
+ mPressOnAppSwitchBehavior, UserHandle.USER_CURRENT);
+ mLongPressOnAppSwitchBehavior = Settings.System.getIntForUser(resolver,
+ Settings.System.KEY_APP_SWITCH_LONG_PRESS_ACTION,
+ mLongPressOnAppSwitchBehavior, UserHandle.USER_CURRENT);
+
+ hasPermanentMenu |= mPressOnAppSwitchBehavior == KEY_ACTION_MENU
+ || mLongPressOnAppSwitchBehavior == KEY_ACTION_MENU;
}
+
+ mHasPermanentMenuKey = hasPermanentMenu;
}
@Override
@@ -1690,7 +2073,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
* navigation bar and touch exploration is not enabled
*/
private boolean canHideNavigationBar() {
- return mHasNavigationBar
+ return hasNavigationBar()
&& !mAccessibilityManager.isTouchExplorationEnabled();
}
@@ -1712,6 +2095,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
public void updateSettings() {
ContentResolver resolver = mContext.getContentResolver();
boolean updateRotation = false;
+ int mDeviceHardwareWakeKeys = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_deviceHardwareWakeKeys);
synchronized (mLock) {
mEndcallBehavior = Settings.System.getIntForUser(resolver,
Settings.System.END_BUTTON_BEHAVIOR,
@@ -1721,6 +2106,33 @@ public class PhoneWindowManager implements WindowManagerPolicy {
Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR,
Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_DEFAULT,
UserHandle.USER_CURRENT);
+ mHomeWakeScreen = (Settings.System.getIntForUser(resolver,
+ Settings.System.HOME_WAKE_SCREEN, 1, UserHandle.USER_CURRENT) == 1) &&
+ ((mDeviceHardwareWakeKeys & KEY_MASK_HOME) != 0);
+ mBackWakeScreen = (Settings.System.getIntForUser(resolver,
+ Settings.System.BACK_WAKE_SCREEN, 0, UserHandle.USER_CURRENT) == 1) &&
+ ((mDeviceHardwareWakeKeys & KEY_MASK_BACK) != 0);
+ mMenuWakeScreen = (Settings.System.getIntForUser(resolver,
+ Settings.System.MENU_WAKE_SCREEN, 0, UserHandle.USER_CURRENT) == 1) &&
+ ((mDeviceHardwareWakeKeys & KEY_MASK_MENU) != 0);
+ mAssistWakeScreen = (Settings.System.getIntForUser(resolver,
+ Settings.System.ASSIST_WAKE_SCREEN, 0, UserHandle.USER_CURRENT) == 1) &&
+ ((mDeviceHardwareWakeKeys & KEY_MASK_ASSIST) != 0);
+ mAppSwitchWakeScreen = (Settings.System.getIntForUser(resolver,
+ Settings.System.APP_SWITCH_WAKE_SCREEN, 0, UserHandle.USER_CURRENT) == 1) &&
+ ((mDeviceHardwareWakeKeys & KEY_MASK_APP_SWITCH) != 0);
+ mCameraWakeScreen = (Settings.System.getIntForUser(resolver,
+ Settings.System.CAMERA_WAKE_SCREEN, 0, UserHandle.USER_CURRENT) == 1) &&
+ ((mDeviceHardwareWakeKeys & KEY_MASK_CAMERA) != 0);
+ mCameraSleepOnRelease = (Settings.System.getIntForUser(resolver,
+ Settings.System.CAMERA_SLEEP_ON_RELEASE, 0, UserHandle.USER_CURRENT) == 1);
+ mCameraLaunch = (Settings.System.getIntForUser(resolver,
+ Settings.System.CAMERA_LAUNCH, 0, UserHandle.USER_CURRENT) == 1);
+ mVolumeWakeScreen = (Settings.System.getIntForUser(resolver,
+ Settings.System.VOLUME_WAKE_SCREEN, 0, UserHandle.USER_CURRENT) == 1) &&
+ ((mDeviceHardwareWakeKeys & KEY_MASK_VOLUME) != 0);
+ mVolBtnMusicControls = (Settings.System.getIntForUser(resolver,
+ Settings.System.VOLBTN_MUSIC_CONTROLS, 1, UserHandle.USER_CURRENT) == 1);
// Configure wake gesture.
boolean wakeGestureEnabledSetting = Settings.Secure.getIntForUser(resolver,
@@ -1731,6 +2143,30 @@ public class PhoneWindowManager implements WindowManagerPolicy {
updateWakeGestureListenerLp();
}
+ final boolean useEdgeService = Settings.System.getIntForUser(resolver,
+ Settings.System.USE_EDGE_SERVICE_FOR_GESTURES, 1, UserHandle.USER_CURRENT) == 1;
+ if (useEdgeService ^ mUsingEdgeGestureServiceForGestures && mSystemReady) {
+ if (!mUsingEdgeGestureServiceForGestures && useEdgeService) {
+ mUsingEdgeGestureServiceForGestures = true;
+ mWindowManagerFuncs.unregisterPointerEventListener(mSystemGestures);
+ } else if (mUsingEdgeGestureServiceForGestures && !useEdgeService) {
+ mUsingEdgeGestureServiceForGestures = false;
+ mWindowManagerFuncs.registerPointerEventListener(mSystemGestures);
+ }
+ updateEdgeGestureListenerState();
+ }
+
+ boolean devForceNavbar = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.DEV_FORCE_SHOW_NAVBAR, 0, UserHandle.USER_CURRENT) == 1;
+ if (devForceNavbar != mDevForceNavbar) {
+ mDevForceNavbar = devForceNavbar;
+ }
+
+ mNavigationBarLeftInLandscape = Settings.System.getIntForUser(resolver,
+ Settings.System.NAVBAR_LEFT_IN_LANDSCAPE, 0, UserHandle.USER_CURRENT) == 1;
+
+ updateKeyAssignments();
+
// Configure rotation lock.
int userRotation = Settings.System.getIntForUser(resolver,
Settings.System.USER_ROTATION, Surface.ROTATION_0,
@@ -1749,6 +2185,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
updateOrientationListenerLp();
}
+ mUserRotationAngles = Settings.System.getInt(resolver,
+ Settings.System.ACCELEROMETER_ROTATION_ANGLES, -1);
+
if (mSystemReady) {
int pointerLocation = Settings.System.getIntForUser(resolver,
Settings.System.POINTER_LOCATION, 0, UserHandle.USER_CURRENT);
@@ -2034,13 +2473,16 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
+ private boolean isBuiltInKeyboardVisible() {
+ return mHaveBuiltInKeyboard && !isHidden(mLidKeyboardAccessibility);
+ }
+
/** {@inheritDoc} */
@Override
public void adjustConfigurationLw(Configuration config, int keyboardPresence,
int navigationPresence) {
mHaveBuiltInKeyboard = (keyboardPresence & PRESENCE_INTERNAL) != 0;
- readConfigurationDependentBehaviors();
readLidState();
applyLidSwitchState();
@@ -2182,7 +2624,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation) {
- if (mHasNavigationBar) {
+ if (hasNavigationBar()) {
// For a basic navigation bar, when we are in landscape mode we place
// the navigation bar to the side.
if (mNavigationBarCanMove && fullWidth > fullHeight) {
@@ -2194,7 +2636,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation) {
- if (mHasNavigationBar) {
+ if (hasNavigationBar()) {
// For a basic navigation bar, when we are in portrait mode we place
// the navigation bar to the bottom.
if (!mNavigationBarCanMove || fullWidth < fullHeight) {
@@ -2492,7 +2934,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (win.getAttrs().windowAnimations != 0) {
return 0;
}
- // This can be on either the bottom or the right.
+ // This can be on either the bottom, left, or the right.
if (mNavigationBarOnBottom) {
if (transit == TRANSIT_EXIT
|| transit == TRANSIT_HIDE) {
@@ -2504,10 +2946,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
} else {
if (transit == TRANSIT_EXIT
|| transit == TRANSIT_HIDE) {
- return R.anim.dock_right_exit;
+ return mNavigationBarLeftInLandscape
+ ? R.anim.dock_left_exit : R.anim.dock_right_exit;
} else if (transit == TRANSIT_ENTER
|| transit == TRANSIT_SHOW) {
- return R.anim.dock_right_enter;
+ return mNavigationBarLeftInLandscape
+ ? R.anim.dock_left_enter : R.anim.dock_right_enter;
}
}
}
@@ -2595,9 +3039,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
- public Animation createForceHideWallpaperExitAnimation(boolean goingToNotificationShade) {
+ public Animation createForceHideWallpaperExitAnimation(boolean goingToNotificationShade,
+ boolean keyguardShowingMedia) {
if (goingToNotificationShade) {
return null;
+ } else if (keyguardShowingMedia) {
+ return AnimationUtils.loadAnimation(mContext, R.anim.lock_screen_wallpaper_exit_noop);
} else {
return AnimationUtils.loadAnimation(mContext, R.anim.lock_screen_wallpaper_exit);
}
@@ -2645,12 +3092,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
final boolean keyguardOn = keyguardOn();
- final int keyCode = event.getKeyCode();
final int repeatCount = event.getRepeatCount();
final int metaState = event.getMetaState();
final int flags = event.getFlags();
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
final boolean canceled = event.isCanceled();
+ final boolean longPress = (flags & KeyEvent.FLAG_LONG_PRESS) != 0;
+ final boolean virtualKey = event.getDeviceId() == KeyCharacterMap.VIRTUAL_KEYBOARD;
+ final int keyCode = event.getKeyCode();
if (DEBUG_INPUT) {
Log.d(TAG, "interceptKeyTi keyCode=" + keyCode + " down=" + down + " repeatCount="
@@ -2685,6 +3134,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mPendingMetaAction = false;
}
+ if (keyCode == KeyEvent.KEYCODE_BACK && !down) {
+ mHandler.removeCallbacks(mBackLongPress);
+ }
+
// First we always handle the home key here, so applications
// can never break it, although if keyguard is on, we do let
// it handle it, because that gives us the correct 5 second
@@ -2694,7 +3147,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// If we have released the home key, and didn't do anything else
// while it was pressed, then it is time to go home!
if (!down) {
- cancelPreloadRecentApps();
+ if (mDoubleTapOnHomeBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
mHomePressed = false;
if (mHomeConsumed) {
@@ -2717,7 +3172,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
// Delay handling home if a double-tap is possible.
- if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_NOTHING) {
+ if (mDoubleTapOnHomeBehavior != KEY_ACTION_NOTHING) {
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
mHomeDoubleTapPending = true;
mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
@@ -2755,44 +3210,83 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (mHomeDoubleTapPending) {
mHomeDoubleTapPending = false;
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable);
- handleDoubleTapOnHome();
- } else if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_SYSTEM_UI
- || mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) {
+ performKeyAction(mDoubleTapOnHomeBehavior, event);
+ mHomeConsumed = mDoubleTapOnHomeBehavior != KEY_ACTION_SLEEP;
+ } else if (mLongPressOnHomeBehavior == KEY_ACTION_APP_SWITCH
+ || mDoubleTapOnHomeBehavior == KEY_ACTION_APP_SWITCH) {
preloadRecentApps();
}
- } else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
- if (!keyguardOn) {
- handleLongPressOnHome(event.getDeviceId());
+ } else if (longPress) {
+ if (!keyguardOn && !mHomeConsumed &&
+ mLongPressOnHomeBehavior != KEY_ACTION_NOTHING) {
+ if (mLongPressOnHomeBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
+ mHomePressed = true;
+ performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+ performKeyAction(mLongPressOnHomeBehavior, event);
+ mHomeConsumed = true;
}
}
return -1;
} else if (keyCode == KeyEvent.KEYCODE_MENU) {
// Hijack modified menu keys for debugging features
final int chordBug = KeyEvent.META_SHIFT_ON;
+ if (virtualKey || keyguardOn) {
+ // Let the app handle the key
+ return 0;
+ }
- if (down && repeatCount == 0) {
- if (mEnableShiftMenuBugReports && (metaState & chordBug) == chordBug) {
- Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
- mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT,
- null, null, null, 0, null, null);
- return -1;
- } else if (SHOW_PROCESSES_ON_ALT_MENU &&
- (metaState & KeyEvent.META_ALT_ON) == KeyEvent.META_ALT_ON) {
- Intent service = new Intent();
- service.setClassName(mContext, "com.android.server.LoadAverageService");
- ContentResolver res = mContext.getContentResolver();
- boolean shown = Settings.Global.getInt(
- res, Settings.Global.SHOW_PROCESSES, 0) != 0;
- if (!shown) {
- mContext.startService(service);
- } else {
- mContext.stopService(service);
+ if (down) {
+ if (mPressOnMenuBehavior == KEY_ACTION_APP_SWITCH
+ || mLongPressOnMenuBehavior == KEY_ACTION_APP_SWITCH) {
+ preloadRecentApps();
+ }
+ if (repeatCount == 0) {
+ mMenuPressed = true;
+ if (mEnableShiftMenuBugReports && (metaState & chordBug) == chordBug) {
+ Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
+ mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT,
+ null, null, null, 0, null, null);
+ return -1;
+ } else if (SHOW_PROCESSES_ON_ALT_MENU &&
+ (metaState & KeyEvent.META_ALT_ON) == KeyEvent.META_ALT_ON) {
+ Intent service = new Intent();
+ service.setClassName(mContext, "com.android.server.LoadAverageService");
+ ContentResolver res = mContext.getContentResolver();
+ boolean shown = Settings.Global.getInt(
+ res, Settings.Global.SHOW_PROCESSES, 0) != 0;
+ if (!shown) {
+ mContext.startService(service);
+ } else {
+ mContext.stopService(service);
+ }
+ Settings.Global.putInt(
+ res, Settings.Global.SHOW_PROCESSES, shown ? 0 : 1);
+ return -1;
+ }
+ } else if (longPress) {
+ if (!keyguardOn && mLongPressOnMenuBehavior != KEY_ACTION_NOTHING) {
+ if (mLongPressOnMenuBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
+ performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+ performKeyAction(mLongPressOnMenuBehavior, event);
+ mMenuPressed = false;
+ return -1;
}
- Settings.Global.putInt(
- res, Settings.Global.SHOW_PROCESSES, shown ? 0 : 1);
- return -1;
}
}
+ if (!down && mMenuPressed) {
+ if (mPressOnMenuBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
+ mMenuPressed = false;
+ if (!canceled) {
+ performKeyAction(mPressOnMenuBehavior, event);
+ }
+ }
+ return -1;
} else if (keyCode == KeyEvent.KEYCODE_SEARCH) {
if (down) {
if (repeatCount == 0) {
@@ -2809,10 +3303,34 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return 0;
} else if (keyCode == KeyEvent.KEYCODE_APP_SWITCH) {
if (!keyguardOn) {
- if (down && repeatCount == 0) {
- preloadRecentApps();
- } else if (!down) {
- toggleRecentApps();
+ if (down) {
+ if (mPressOnAppSwitchBehavior == KEY_ACTION_APP_SWITCH
+ || mLongPressOnAppSwitchBehavior == KEY_ACTION_APP_SWITCH) {
+ preloadRecentApps();
+ }
+ if (repeatCount == 0) {
+ mAppSwitchLongPressed = false;
+ } else if (longPress) {
+ if (mLongPressOnAppSwitchBehavior != KEY_ACTION_NOTHING) {
+ if (mLongPressOnAppSwitchBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
+ performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+ performKeyAction(mLongPressOnAppSwitchBehavior, event);
+ mAppSwitchLongPressed = true;
+ }
+ }
+ } else {
+ if (mAppSwitchLongPressed) {
+ mAppSwitchLongPressed = false;
+ } else {
+ if (mPressOnAppSwitchBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
+ if (!canceled) {
+ performKeyAction(mPressOnAppSwitchBehavior, event);
+ }
+ }
}
}
return -1;
@@ -2829,20 +3347,31 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
} else if (keyCode == KeyEvent.KEYCODE_ASSIST) {
if (down) {
+ if (mPressOnAssistBehavior == KEY_ACTION_APP_SWITCH
+ || mLongPressOnAssistBehavior == KEY_ACTION_APP_SWITCH) {
+ preloadRecentApps();
+ }
if (repeatCount == 0) {
mAssistKeyLongPressed = false;
- } else if (repeatCount == 1) {
- mAssistKeyLongPressed = true;
- if (!keyguardOn) {
- launchAssistLongPressAction();
+ } else if (longPress) {
+ if (!keyguardOn && mLongPressOnAssistBehavior != KEY_ACTION_NOTHING) {
+ if (mLongPressOnAssistBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
+ performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+ performKeyAction(mLongPressOnAssistBehavior, event);
+ mAssistKeyLongPressed = true;
}
}
} else {
if (mAssistKeyLongPressed) {
mAssistKeyLongPressed = false;
} else {
- if (!keyguardOn) {
- launchAssistAction(null, event.getDeviceId());
+ if (mPressOnAssistBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
+ if (!canceled) {
+ performKeyAction(mPressOnAssistBehavior, event);
}
}
}
@@ -2915,6 +3444,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD, event.getDeviceId());
}
return -1;
+ } else if (keyCode == KeyEvent.KEYCODE_BACK) {
+ if (Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.KILL_APP_LONGPRESS_BACK, 0) == 1) {
+ if (down && repeatCount == 0) {
+ mHandler.postDelayed(mBackLongPress, mBackKillTimeout);
+ }
+ }
}
// Shortcuts are invoked through Search+key, so intercept those here
@@ -3023,6 +3559,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return -1;
}
+ // Specific device key handling
+ if (mDeviceKeyHandler != null) {
+ try {
+ // The device only should consume known keys.
+ if (mDeviceKeyHandler.handleKeyEvent(event)) {
+ return -1;
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Could not dispatch event to device key handler", e);
+ }
+ }
+
// Reserve all the META modifier combos for system behavior
if ((metaState & KeyEvent.META_META_ON) != 0) {
return -1;
@@ -3054,6 +3602,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN
&& event.getRepeatCount() == 0;
+ // Specific device key handling
+ if (mDeviceKeyHandler != null) {
+ try {
+ // The device only should consume known keys.
+ if (mDeviceKeyHandler.handleKeyEvent(event)) {
+ return null;
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Could not dispatch event to device key handler", e);
+ }
+ }
+
// Check for fallback actions specified by the key character map.
final FallbackAction fallbackAction;
if (initialDown) {
@@ -3308,8 +3868,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
hideRecentApps(false, true);
- } else {
- // Otherwise, just launch Home
+ } else if (mScreenOnFully) {
+ // check if screen is fully on before going home
+ // to avoid hardware home button wake going home
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
startDockOrHome(true /*fromHomeKey*/, awakenFromDreams);
}
@@ -3397,6 +3958,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
mRecentsVisible = (visibility & View.RECENT_APPS_VISIBLE) > 0;
+ updateEdgeGestureListenerState();
+
// Reset any bits in mForceClearingStatusBarVisibility that
// are now clear.
mResettingSystemUiFlags &= visibility;
@@ -3620,6 +4183,34 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// we can tell the app that it is covered by it.
mSystemBottom = mTmpNavigationFrame.top;
}
+ } else if (mNavigationBarLeftInLandscape) {
+ // Landscape screen; nav bar goes to the left.
+ int right = overscanLeft + mNavigationBarWidthForRotation[displayRotation];
+ mTmpNavigationFrame.set(0, 0, right, displayHeight);
+ mStableLeft = mStableFullscreenLeft = mTmpNavigationFrame.right;
+ mStableFullscreenLeft = mTmpNavigationFrame.right;
+ if (transientNavBarShowing) {
+ mNavigationBarController.setBarShowingLw(true);
+ } else if (navVisible) {
+ mNavigationBarController.setBarShowingLw(true);
+ mDockLeft = mTmpNavigationFrame.right;
+ mRestrictedScreenLeft = mDockLeft;
+ mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;
+ mRestrictedOverscanScreenLeft = mRestrictedScreenLeft;
+ mRestrictedOverscanScreenWidth = mDockRight
+ - mRestrictedOverscanScreenLeft;
+ } else {
+ // We currently want to hide the navigation UI.
+ mNavigationBarController.setBarShowingLw(false);
+ }
+
+ if (navVisible && !navTranslucent && !mNavigationBar.isAnimatingLw()
+ && !mNavigationBarController.wasRecentlyTranslucent()) {
+ // If the nav bar is currently requested to be visible,
+ // and not in the process of animating on or off, then
+ // we can tell the app that it is covered by it.
+ mSystemLeft = mTmpNavigationFrame.right;
+ }
} else {
// Landscape screen; nav bar goes to the right.
int left = displayWidth - overscanRight
@@ -4666,6 +5257,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// update since mAllowLockscreenWhenOn might have changed
updateLockScreenTimeout();
+ updateEdgeGestureListenerState();
return changes;
}
@@ -4808,6 +5400,20 @@ public class PhoneWindowManager implements WindowManagerPolicy {
setHdmiPlugged(!mHdmiPlugged);
}
+ /**
+ * @return Whether music is being played right now "locally" (e.g. on the device's speakers
+ * or wired headphones) or "remotely" (e.g. on a device using the Cast protocol and
+ * controlled by this device, or through remote submix).
+ */
+ private boolean isMusicActive() {
+ final AudioManager am = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
+ if (am == null) {
+ Log.w(TAG, "isMusicActive: couldn't get AudioManager reference");
+ return false;
+ }
+ return am.isMusicActive();
+ }
+
final Object mScreenshotLock = new Object();
ServiceConnection mScreenshotConnection = null;
@@ -4889,6 +5495,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
final boolean canceled = event.isCanceled();
final int keyCode = event.getKeyCode();
+ final int scanCode = event.getScanCode();
final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;
@@ -4920,8 +5527,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// If we're currently dozing with the screen on and the keyguard showing, pass the key
// to the application but preserve its wake key status to make sure we still move
// from dozing to fully interactive if we would normally go from off to fully
- // interactive.
+ // interactive, unless the user has explicitly disabled this wake key.
result = ACTION_PASS_TO_USER;
+ isWakeKey = isWakeKey && isWakeKeyEnabled(keyCode);
} else {
// When the screen is off and the key is not injected, determine whether
// to wake the device but don't pass the key to the application.
@@ -4954,6 +5562,17 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// On TVs volume keys never go to the foreground app
result &= ~ACTION_PASS_TO_USER;
}
+ // Eat all down & up keys when using volume wake.
+ // This disables volume control, music control, and "beep" on key up.
+ if (isWakeKey && mVolumeWakeScreen) {
+ mVolumeWakeTriggered = true;
+ break;
+ } else if (mVolumeWakeTriggered && !down) {
+ result &= ~ACTION_PASS_TO_USER;
+ mVolumeWakeTriggered = false;
+ break;
+ }
+
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
if (down) {
if (interactive && !mScreenshotChordVolumeDownKeyTriggered
@@ -5013,23 +5632,101 @@ public class PhoneWindowManager implements WindowManagerPolicy {
break;
}
}
+ }
+
+ // Disable music and volume control when used as wake key
+ if ((result & ACTION_PASS_TO_USER) == 0 && !mVolumeWakeScreen) {
+ boolean mayChangeVolume = false;
+
+ if (isMusicActive()) {
+ if (mVolBtnMusicControls && (keyCode != KeyEvent.KEYCODE_VOLUME_MUTE)) {
+ // Detect long key presses.
+ if (down) {
+ mIsLongPress = false;
+ // TODO: Long press of MUTE could be mapped to KEYCODE_MEDIA_PLAY_PAUSE
+ int newKeyCode = event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP ?
+ KeyEvent.KEYCODE_MEDIA_NEXT : KeyEvent.KEYCODE_MEDIA_PREVIOUS;
+ scheduleLongPressKeyEvent(event, newKeyCode);
+ // Consume key down events of all presses.
+ break;
+ } else {
+ mHandler.removeMessages(MSG_DISPATCH_VOLKEY_WITH_WAKE_LOCK);
+ // Consume key up events of long presses only.
+ if (mIsLongPress) {
+ break;
+ }
+ // Change volume only on key up events of short presses.
+ mayChangeVolume = true;
+ }
+ } else {
+ // Long key press detection not applicable, change volume only
+ // on key down events
+ mayChangeVolume = down;
+ }
+ }
- if ((result & ACTION_PASS_TO_USER) == 0) {
+ if (mayChangeVolume) {
if (mUseTvRouting) {
dispatchDirectAudioEvent(event);
} else {
// If we aren't passing to the user and no one else
- // handled it send it to the session manager to
- // figure out.
+ // handled it send it to the session manager to figure
+ // out.
+
+ // Rewrite the event to use key-down as sendVolumeKeyEvent will
+ // only change the volume on key down.
+ KeyEvent newEvent = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
MediaSessionLegacyHelper.getHelper(mContext)
- .sendVolumeKeyEvent(event, true);
+ .sendVolumeKeyEvent(newEvent, true);
}
- break;
}
+ break;
}
break;
}
+ case KeyEvent.KEYCODE_HOME:
+ if (down && !interactive && mHomeWakeScreen) {
+ isWakeKey = true;
+ }
+ break;
+
+ case KeyEvent.KEYCODE_FOCUS:
+ if (down && !interactive && mCameraSleepOnRelease) {
+ mIsFocusPressed = true;
+ } else if ((event.getAction() == KeyEvent.ACTION_UP)
+ && mScreenOnFully && mIsFocusPressed) {
+ // Check if screen is fully on before letting the device go to sleep
+ mPowerManager.goToSleep(SystemClock.uptimeMillis());
+ mIsFocusPressed = false;
+ }
+ break;
+
+ case KeyEvent.KEYCODE_CAMERA:
+ if (down && mIsFocusPressed) {
+ mIsFocusPressed = false;
+ }
+ if (down) {
+ mIsLongPress = false;
+ scheduleLongPressKeyEvent(event, KeyEvent.KEYCODE_CAMERA);
+ // Consume key down events of all presses.
+ break;
+ } else {
+ mHandler.removeMessages(MSG_CAMERA_LONG_PRESS);
+ // Consume key up events of long presses only.
+ if (mIsLongPress && mCameraLaunch) {
+ Intent intent;
+ if (keyguardActive) {
+ intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE);
+ } else {
+ intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
+ }
+ isWakeKey = true;
+ startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+ }
+ }
+ break;
+
case KeyEvent.KEYCODE_ENDCALL: {
result &= ~ACTION_PASS_TO_USER;
if (down) {
@@ -5068,6 +5765,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
case KeyEvent.KEYCODE_POWER: {
+ if ((mTopFullscreenOpaqueWindowState.getAttrs().privateFlags
+ & WindowManager.LayoutParams.PRIVATE_FLAG_PREVENT_POWER_KEY) != 0
+ && mScreenOnFully) {
+ return result;
+ }
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false; // wake-up will be handled separately
if (down) {
@@ -5173,6 +5875,19 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return result;
}
+ private void scheduleLongPressKeyEvent(KeyEvent origEvent, int keyCode) {
+ KeyEvent event = new KeyEvent(origEvent.getDownTime(), origEvent.getEventTime(),
+ origEvent.getAction(), keyCode, 0);
+ Message msg;
+ if (keyCode == KeyEvent.KEYCODE_CAMERA) {
+ msg = mHandler.obtainMessage(MSG_CAMERA_LONG_PRESS, event);
+ } else {
+ msg = mHandler.obtainMessage(MSG_DISPATCH_VOLKEY_WITH_WAKE_LOCK, event);
+ }
+ msg.setAsynchronous(true);
+ mHandler.sendMessageDelayed(msg, ViewConfiguration.getLongPressTimeout());
+ }
+
/**
* Returns true if the key can have global actions attached to it.
* We reserve all power management keys for the system since they require
@@ -5190,6 +5905,32 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
/**
+ * Check if the given keyCode represents a key that is considered a wake key
+ * and is currently enabled by the user in Settings or for another reason.
+ */
+ private boolean isWakeKeyEnabled(int keyCode) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_VOLUME_UP:
+ case KeyEvent.KEYCODE_VOLUME_DOWN:
+ case KeyEvent.KEYCODE_VOLUME_MUTE:
+ // Volume keys are still wake keys if the device is docked.
+ return mVolumeWakeScreen || mDockMode != Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ case KeyEvent.KEYCODE_BACK:
+ return mBackWakeScreen;
+ case KeyEvent.KEYCODE_MENU:
+ return mMenuWakeScreen;
+ case KeyEvent.KEYCODE_ASSIST:
+ return mAssistWakeScreen;
+ case KeyEvent.KEYCODE_APP_SWITCH:
+ return mAppSwitchWakeScreen;
+ case KeyEvent.KEYCODE_CAMERA:
+ case KeyEvent.KEYCODE_FOCUS:
+ return mCameraWakeScreen;
+ }
+ return true;
+ }
+
+ /**
* When the screen is off we ignore some keys that might otherwise typically
* be considered wake keys. We filter them out here.
*
@@ -5202,9 +5943,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_MUTE:
- return mDockMode != Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ return mVolumeWakeScreen || mDockMode != Intent.EXTRA_DOCK_STATE_UNDOCKED;
- // ignore media and camera keys
+ // ignore media keys
case KeyEvent.KEYCODE_MUTE:
case KeyEvent.KEYCODE_HEADSETHOOK:
case KeyEvent.KEYCODE_MEDIA_PLAY:
@@ -5217,8 +5958,19 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case KeyEvent.KEYCODE_MEDIA_RECORD:
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK:
- case KeyEvent.KEYCODE_CAMERA:
return false;
+
+ case KeyEvent.KEYCODE_BACK:
+ return mBackWakeScreen;
+ case KeyEvent.KEYCODE_MENU:
+ return mMenuWakeScreen;
+ case KeyEvent.KEYCODE_ASSIST:
+ return mAssistWakeScreen;
+ case KeyEvent.KEYCODE_APP_SWITCH:
+ return mAppSwitchWakeScreen;
+ case KeyEvent.KEYCODE_CAMERA:
+ case KeyEvent.KEYCODE_FOCUS:
+ return mCameraWakeScreen;
}
return true;
}
@@ -5263,7 +6015,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
IDreamManager dreamManager = getDreamManager();
try {
- if (dreamManager != null && dreamManager.isDreaming()) {
+ if (dreamManager != null && dreamManager.isDreaming() && !dreamManager.isDozing()) {
return true;
}
} catch (RemoteException e) {
@@ -5425,6 +6177,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// current user.
mSettingsObserver.onChange(false);
+ if (mGlobalActions != null) {
+ mGlobalActions.updatePowerMenuActions();
+ }
+
// force a re-application of focused window sysui visibility.
// the window may never have been shown for this user
// e.g. the keyguard when going through the new-user setup flow
@@ -5684,10 +6440,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (!mKeyguardDrawnOnce && mAwake) {
mKeyguardDrawnOnce = true;
enableScreen = true;
- if (mBootMessageNeedsHiding) {
- mBootMessageNeedsHiding = false;
- hideBootMessages();
- }
} else {
enableScreen = false;
}
@@ -5707,9 +6459,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private void handleHideBootMessage() {
synchronized (mLock) {
- if (!mKeyguardDrawnOnce) {
- mBootMessageNeedsHiding = true;
- return; // keyguard hasn't drawn the first time yet, not done booting
+ if (!mKeyguardDrawComplete) {
+ return; // keyguard hasn't completed drawing, not done booting.
}
}
@@ -5842,8 +6593,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
final int preferredRotation;
- if (mLidState == LID_OPEN && mLidOpenRotation >= 0) {
- // Ignore sensor when lid switch is open and rotation is forced.
+ if ((mLidState == LID_OPEN && mLidOpenRotation >= 0)
+ && !(mHasRemovableLid
+ && mDockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED)) {
+ // Ignore sensor when lid switch is open and rotation is forced
+ // and a removable lid was not undocked.
preferredRotation = mLidOpenRotation;
} else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR
&& (mCarDockEnablesAccelerometer || mCarDockRotation >= 0)) {
@@ -5905,10 +6659,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mAllowAllRotations = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_allowAllRotations) ? 1 : 0;
}
- if (sensorRotation != Surface.ROTATION_180
- || mAllowAllRotations == 1
- || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
- || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {
+
+ // use sensor orientation if it's forced, or if the user has allowed it
+ boolean useSensorRotation =
+ orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
+ || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER
+ || RotationPolicy.isRotationAllowed(sensorRotation, mUserRotationAngles,
+ mAllowAllRotations != 0);
+ if (useSensorRotation) {
preferredRotation = sensorRotation;
} else {
preferredRotation = lastRotation;
@@ -6076,6 +6834,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mKeyguardDelegate = new KeyguardServiceDelegate(mContext);
mKeyguardDelegate.onSystemReady();
+ mEdgeGestureManager = EdgeGestureManager.getInstance();
+ mEdgeGestureManager.setEdgeGestureActivationListener(mEdgeGestureActivationListener);
+
readCameraLensCoverState();
updateUiMode();
boolean bindKeyguardNow;
@@ -6309,14 +7070,37 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
private void applyLidSwitchState() {
- if (mLidState == LID_CLOSED && mLidControlsSleep) {
- mPowerManager.goToSleep(SystemClock.uptimeMillis(),
- PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH,
- PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
- }
+ mPowerManager.setKeyboardVisibility(isBuiltInKeyboardVisible());
+ if (mLidControlsSleep) {
+ IDreamManager dreamManager = getDreamManager();
+ if (dreamManager != null) {
+ try {
+ dreamManager.setLidState(mLidState);
+ } catch (RemoteException e) {
+ }
+ }
- synchronized (mLock) {
- updateWakeGestureListenerLp();
+ if (mLidState == LID_CLOSED) {
+ if (mFocusedWindow != null && (mFocusedWindow.getAttrs().flags
+ & WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) != 0) {
+ // if an application requests that the screen be turned on
+ // and there's a closed device cover, don't turn the screen off!
+ return;
+ }
+
+ TelecomManager telephonyService = getTelecommService();
+ if (!(telephonyService == null
+ || telephonyService.isRinging())) {
+ mPowerManager.goToSleep(SystemClock.uptimeMillis(),
+ PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH,
+ PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
+ }
+
+ }
+
+ synchronized (mLock) {
+ updateWakeGestureListenerLp();
+ }
}
}
@@ -6391,7 +7175,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
ActivityInfo ai = null;
ResolveInfo info = mContext.getPackageManager().resolveActivityAsUser(
intent,
- PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,
+ PackageManager.MATCH_DEFAULT_ONLY
+ | PackageManager.GET_META_DATA
+ | PackageManager.GET_RESOLVED_FILTER,
mCurrentUserId);
if (info != null) {
ai = info.activityInfo;
@@ -6798,6 +7584,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// overridden by qemu.hw.mainkeys in the emulator.
@Override
public boolean hasNavigationBar() {
+ return mHasNavigationBar || mDevForceNavbar;
+ }
+
+ @Override
+ public boolean hasPermanentMenuKey() {
+ return !hasNavigationBar() && mHasPermanentMenuKey;
+ }
+
+ public boolean needsNavigationBar() {
return mHasNavigationBar;
}
diff --git a/services/core/java/com/android/server/policy/PolicyControl.java b/services/core/java/com/android/server/policy/PolicyControl.java
index dbafc42..0f6fc58 100644
--- a/services/core/java/com/android/server/policy/PolicyControl.java
+++ b/services/core/java/com/android/server/policy/PolicyControl.java
@@ -125,6 +125,10 @@ public class PolicyControl {
}
}
+ public static boolean isImmersiveFiltersActive() {
+ return sImmersiveStatusFilter != null || sImmersiveNavigationFilter != null;
+ }
+
public static void dump(String prefix, PrintWriter pw) {
dump("sImmersiveStatusFilter", sImmersiveStatusFilter, prefix, pw);
dump("sImmersiveNavigationFilter", sImmersiveNavigationFilter, prefix, pw);
diff --git a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
index 627b328..31fc653 100644
--- a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
+++ b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
@@ -36,6 +36,7 @@ public class SystemGesturesPointerEventListener implements PointerEventListener
private static final int SWIPE_FROM_TOP = 1;
private static final int SWIPE_FROM_BOTTOM = 2;
private static final int SWIPE_FROM_RIGHT = 3;
+ private static final int SWIPE_FROM_LEFT = 4;
private final int mSwipeStartThreshold;
private final int mSwipeDistanceThreshold;
@@ -100,6 +101,9 @@ public class SystemGesturesPointerEventListener implements PointerEventListener
} else if (swipe == SWIPE_FROM_RIGHT) {
if (DEBUG) Slog.d(TAG, "Firing onSwipeFromRight");
mCallbacks.onSwipeFromRight();
+ } else if (swipe == SWIPE_FROM_LEFT) {
+ if (DEBUG) Slog.d(TAG, "Firing onSwipeFromLeft");
+ mCallbacks.onSwipeFromLeft();
}
}
break;
@@ -182,6 +186,11 @@ public class SystemGesturesPointerEventListener implements PointerEventListener
&& elapsed < SWIPE_TIMEOUT_MS) {
return SWIPE_FROM_BOTTOM;
}
+ if (fromX <= mSwipeStartThreshold
+ && x > fromX + mSwipeDistanceThreshold
+ && elapsed < SWIPE_TIMEOUT_MS) {
+ return SWIPE_FROM_LEFT;
+ }
if (fromX >= screenWidth - mSwipeStartThreshold
&& x < fromX - mSwipeDistanceThreshold
&& elapsed < SWIPE_TIMEOUT_MS) {
@@ -196,6 +205,7 @@ public class SystemGesturesPointerEventListener implements PointerEventListener
void onSwipeFromRight();
void onDown();
void onUpOrCancel();
+ void onSwipeFromLeft();
void onDebug();
}
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 7d64096..3682950 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -18,10 +18,12 @@ package com.android.server.power;
import android.app.ActivityManager;
import android.util.SparseIntArray;
+
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
import com.android.server.EventLogTags;
+import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.am.BatteryStatsService;
@@ -29,6 +31,8 @@ import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
import com.android.server.Watchdog;
+import cyanogenmod.power.PerformanceManagerInternal;
+
import android.Manifest;
import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
@@ -39,6 +43,9 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.SystemSensorManager;
import android.hardware.display.DisplayManagerInternal;
@@ -63,6 +70,7 @@ import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
+import android.telephony.TelephonyManager;
import android.util.EventLog;
import android.util.Slog;
import android.util.TimeUtils;
@@ -99,6 +107,8 @@ public final class PowerManagerService extends SystemService
// Message: Sent when the screen brightness boost expires.
private static final int MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT = 3;
+ private static final int MSG_WAKE_UP = 5;
+
// Dirty bit: mWakeLocks changed
private static final int DIRTY_WAKE_LOCKS = 1 << 0;
// Dirty bit: mWakefulness changed
@@ -156,9 +166,18 @@ public final class PowerManagerService extends SystemService
// Power features defined in hardware/libhardware/include/hardware/power.h.
private static final int POWER_FEATURE_DOUBLE_TAP_TO_WAKE = 1;
+ private static final int DEFAULT_BUTTON_ON_DURATION = 5 * 1000;
+
// Default setting for double tap to wake.
private static final int DEFAULT_DOUBLE_TAP_TO_WAKE = 0;
+ private static final int BUTTON_ON_DURATION = 5 * 1000;
+
+ private static final float PROXIMITY_NEAR_THRESHOLD = 5.0f;
+
+ // Max time (microseconds) to allow a CPU boost for
+ private static final int MAX_CPU_BOOST_TIME = 5000000;
+
private final Context mContext;
private final ServiceThread mHandlerThread;
private final PowerManagerHandler mHandler;
@@ -174,6 +193,16 @@ public final class PowerManagerService extends SystemService
private SettingsObserver mSettingsObserver;
private DreamManagerInternal mDreamManager;
private Light mAttentionLight;
+ private Light mButtonsLight;
+ private Light mKeyboardLight;
+ private Light mCapsLight;
+ private Light mFnLight;
+
+ private int mButtonTimeout;
+ private int mButtonBrightness;
+ private int mButtonBrightnessSettingDefault;
+ private int mKeyboardBrightness;
+ private int mKeyboardBrightnessSettingDefault;
private final Object mLock = new Object();
@@ -351,6 +380,9 @@ public final class PowerManagerService extends SystemService
// Whether device supports double tap to wake.
private boolean mSupportsDoubleTapWakeConfig;
+ // Default value for proximity prevent accidental wakeups
+ private boolean mProximityWakeEnabledByDefaultConfig;
+
// The screen off timeout setting value in milliseconds.
private int mScreenOffTimeoutSetting;
@@ -362,9 +394,12 @@ public final class PowerManagerService extends SystemService
private int mMaximumScreenOffTimeoutFromDeviceAdmin = Integer.MAX_VALUE;
// The stay on while plugged in setting.
- // A bitfield of battery conditions under which to make the screen stay on.
+ // 0: Not enabled; 1: debugging over usb; >2: charging.
private int mStayOnWhilePluggedInSetting;
+ // True if the device should wake up when plugged or unplugged
+ private int mWakeUpWhenPluggedOrUnpluggedSetting;
+
// True if the device should stay on.
private boolean mStayOn;
@@ -393,6 +428,11 @@ public final class PowerManagerService extends SystemService
// Use -1 to disable.
private int mScreenBrightnessOverrideFromWindowManager = -1;
+ // The button brightness setting override from the window manager
+ // to allow the current foreground activity to override the button brightness.
+ // Use -1 to disable.
+ private int mButtonBrightnessOverrideFromWindowManager = -1;
+
// The user activity timeout override from the window manager
// to allow the current foreground activity to override the user activity timeout.
// Use -1 to disable.
@@ -464,6 +504,19 @@ public final class PowerManagerService extends SystemService
private static native void nativeSetAutoSuspend(boolean enable);
private static native void nativeSendPowerHint(int hintId, int data);
private static native void nativeSetFeature(int featureId, int data);
+ private static native int nativeGetFeature(int featureId);
+
+ private boolean mKeyboardVisible = false;
+
+ private SensorManager mSensorManager;
+ private Sensor mProximitySensor;
+ private boolean mProximityWakeEnabled;
+ private int mProximityTimeOut;
+ private boolean mProximityWakeSupported;
+ android.os.PowerManager.WakeLock mProximityWakeLock;
+ SensorEventListener mProximityListener;
+
+ private PerformanceManagerInternal mPerf;
public PowerManagerService(Context context) {
super(context);
@@ -509,6 +562,7 @@ public final class PowerManagerService extends SystemService
userActivityNoUpdateLocked(
now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
updatePowerStateLocked();
+ mPerf = LocalServices.getService(PerformanceManagerInternal.class);
}
}
}
@@ -526,6 +580,8 @@ public final class PowerManagerService extends SystemService
mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting();
mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting();
mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting();
+ mButtonBrightnessSettingDefault = pm.getDefaultButtonBrightness();
+ mKeyboardBrightnessSettingDefault = pm.getDefaultKeyboardBrightness();
SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper());
@@ -543,11 +599,19 @@ public final class PowerManagerService extends SystemService
mLightsManager = getLocalService(LightsManager.class);
mAttentionLight = mLightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
+ mButtonsLight = mLightsManager.getLight(LightsManager.LIGHT_ID_BUTTONS);
+ mKeyboardLight = mLightsManager.getLight(LightsManager.LIGHT_ID_KEYBOARD);
+ mCapsLight = mLightsManager.getLight(LightsManager.LIGHT_ID_CAPS);
+ mFnLight = mLightsManager.getLight(LightsManager.LIGHT_ID_FUNC);
// Initialize display power management.
mDisplayManagerInternal.initPowerManagement(
mDisplayPowerCallbacks, mHandler, sensorManager);
+ // Initialize proximity sensor
+ mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
+ mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+
// Register for broadcasts from other components of the system.
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
@@ -608,6 +672,22 @@ public final class PowerManagerService extends SystemService
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.DOUBLE_TAP_TO_WAKE),
false, mSettingsObserver, UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.BUTTON_BRIGHTNESS),
+ false, mSettingsObserver, UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.KEYBOARD_BRIGHTNESS),
+ false, mSettingsObserver, UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.BUTTON_BACKLIGHT_TIMEOUT),
+ false, mSettingsObserver, UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.PROXIMITY_ON_WAKE),
+ false, mSettingsObserver, UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.WAKE_WHEN_PLUGGED_OR_UNPLUGGED),
+ false, mSettingsObserver, UserHandle.USER_ALL);
+
// Go.
readConfigurationLocked();
updateSettingsLocked();
@@ -655,6 +735,17 @@ public final class PowerManagerService extends SystemService
com.android.internal.R.fraction.config_maximumScreenDimRatio, 1, 1);
mSupportsDoubleTapWakeConfig = resources.getBoolean(
com.android.internal.R.bool.config_supportDoubleTapWake);
+ mProximityTimeOut = resources.getInteger(
+ com.android.internal.R.integer.config_proximityCheckTimeout);
+ mProximityWakeSupported = resources.getBoolean(
+ com.android.internal.R.bool.config_proximityCheckOnWake);
+ mProximityWakeEnabledByDefaultConfig = resources.getBoolean(
+ com.android.internal.R.bool.config_proximityCheckOnWakeEnabledByDefault);
+ if (mProximityWakeSupported) {
+ PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mProximityWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ "ProximityWakeLock");
+ }
}
private void updateSettingsLocked() {
@@ -679,9 +770,14 @@ public final class PowerManagerService extends SystemService
Settings.Secure.SLEEP_TIMEOUT, DEFAULT_SLEEP_TIMEOUT,
UserHandle.USER_CURRENT);
mStayOnWhilePluggedInSetting = Settings.Global.getInt(resolver,
- Settings.Global.STAY_ON_WHILE_PLUGGED_IN, BatteryManager.BATTERY_PLUGGED_AC);
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
mTheaterModeEnabled = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.THEATER_MODE_ON, 0) == 1;
+ mWakeUpWhenPluggedOrUnpluggedSetting = Settings.Global.getInt(resolver,
+ Settings.Global.WAKE_WHEN_PLUGGED_OR_UNPLUGGED,
+ (mWakeUpWhenPluggedOrUnpluggedConfig ? 1 : 0));
+ mProximityWakeEnabled = Settings.System.getInt(resolver,
+ Settings.System.PROXIMITY_ON_WAKE, mProximityWakeEnabledByDefaultConfig ? 1 : 0) == 1;
if (mSupportsDoubleTapWakeConfig) {
boolean doubleTapWakeEnabled = Settings.Secure.getIntForUser(resolver,
@@ -725,6 +821,17 @@ public final class PowerManagerService extends SystemService
updateLowPowerModeLocked();
}
+ mButtonTimeout = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.BUTTON_BACKLIGHT_TIMEOUT,
+ DEFAULT_BUTTON_ON_DURATION, UserHandle.USER_CURRENT);
+
+ mButtonBrightness = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.BUTTON_BRIGHTNESS, mButtonBrightnessSettingDefault,
+ UserHandle.USER_CURRENT);
+ mKeyboardBrightness = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.KEYBOARD_BRIGHTNESS, mKeyboardBrightnessSettingDefault,
+ UserHandle.USER_CURRENT);
+
mDirty |= DIRTY_SETTINGS;
}
@@ -736,6 +843,7 @@ public final class PowerManagerService extends SystemService
// Turn setting off if powered
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.LOW_POWER_MODE, 0);
+ // update performance profile
mLowPowerModeSetting = false;
}
final boolean autoLowPowerModeEnabled = !mIsPowered && mAutoLowPowerModeConfigured
@@ -1389,7 +1497,7 @@ public final class PowerManagerService extends SystemService
private boolean shouldWakeUpWhenPluggedOrUnpluggedLocked(
boolean wasPowered, int oldPlugType, boolean dockedOnWirelessCharger) {
// Don't wake when powered unless configured to do so.
- if (!mWakeUpWhenPluggedOrUnpluggedConfig) {
+ if (mWakeUpWhenPluggedOrUnpluggedSetting == 0) {
return false;
}
@@ -1431,7 +1539,16 @@ public final class PowerManagerService extends SystemService
final boolean wasStayOn = mStayOn;
if (mStayOnWhilePluggedInSetting != 0
&& !isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) {
- mStayOn = mBatteryManagerInternal.isPowered(mStayOnWhilePluggedInSetting);
+ switch (mStayOnWhilePluggedInSetting) {
+ case 1: // Debugging only over usb
+ mStayOn = ((mPlugType & BatteryManager.BATTERY_PLUGGED_USB) != 0)
+ && Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.ADB_ENABLED, 0) != 0;;
+ break;
+ default: // charging
+ mStayOn = mIsPowered;
+ break;
+ }
} else {
mStayOn = false;
}
@@ -1544,10 +1661,38 @@ public final class PowerManagerService extends SystemService
+ screenOffTimeout - screenDimDuration;
if (now < nextTimeout) {
mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
+ if (mWakefulness == WAKEFULNESS_AWAKE) {
+ int buttonBrightness, keyboardBrightness;
+ if (mButtonBrightnessOverrideFromWindowManager >= 0) {
+ buttonBrightness = mButtonBrightnessOverrideFromWindowManager;
+ keyboardBrightness = mButtonBrightnessOverrideFromWindowManager;
+ } else {
+ buttonBrightness = mButtonBrightness;
+ keyboardBrightness = mKeyboardBrightness;
+ }
+
+ mKeyboardLight.setBrightness(mKeyboardVisible ?
+ keyboardBrightness : 0);
+ if (mButtonTimeout != 0
+ && now > mLastUserActivityTime + mButtonTimeout) {
+ mButtonsLight.setBrightness(0);
+ } else {
+ if (!mProximityPositive) {
+ mButtonsLight.setBrightness(buttonBrightness);
+ if (buttonBrightness != 0 && mButtonTimeout != 0) {
+ nextTimeout = now + mButtonTimeout;
+ }
+ }
+ }
+ }
} else {
nextTimeout = mLastUserActivityTime + screenOffTimeout;
if (now < nextTimeout) {
mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
+ if (mWakefulness == WAKEFULNESS_AWAKE) {
+ mButtonsLight.setBrightness(0);
+ mKeyboardLight.setBrightness(0);
+ }
}
}
}
@@ -1811,7 +1956,7 @@ public final class PowerManagerService extends SystemService
}
// Dream has ended or will be stopped. Update the power state.
- if (isItBedTimeYetLocked()) {
+ if (isItBedTimeYetLocked() && !mDreamsActivatedOnSleepByDefaultConfig) {
goToSleepNoUpdateLocked(SystemClock.uptimeMillis(),
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
updatePowerStateLocked();
@@ -2831,6 +2976,10 @@ public final class PowerManagerService extends SystemService
case MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT:
handleScreenBrightnessBoostTimeout();
break;
+ case MSG_WAKE_UP:
+ cleanupProximity();
+ ((Runnable) msg.obj).run();
+ break;
}
}
}
@@ -3010,6 +3159,22 @@ public final class PowerManagerService extends SystemService
}
}
+ private void cleanupProximity() {
+ synchronized (mProximityWakeLock) {
+ cleanupProximityLocked();
+ }
+ }
+
+ private void cleanupProximityLocked() {
+ if (mProximityWakeLock.isHeld()) {
+ mProximityWakeLock.release();
+ }
+ if (mProximityListener != null) {
+ mSensorManager.unregisterListener(mProximityListener);
+ mProximityListener = null;
+ }
+ }
+
private final class BinderService extends IPowerManager.Stub {
@Override // Binder call
public void acquireWakeLockWithUid(IBinder lock, int flags, String tag,
@@ -3165,7 +3330,46 @@ public final class PowerManagerService extends SystemService
}
@Override // Binder call
- public void wakeUp(long eventTime, String reason, String opPackageName) {
+ public void setKeyboardVisibility(boolean visible) {
+ synchronized (mLock) {
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "setKeyboardVisibility: " + visible);
+ }
+ if (mKeyboardVisible != visible) {
+ mKeyboardVisible = visible;
+ if (!visible) {
+ // If hiding keyboard, turn off leds
+ setKeyboardLight(false, 1);
+ setKeyboardLight(false, 2);
+ }
+ synchronized (mLock) {
+ mDirty |= DIRTY_USER_ACTIVITY;
+ updatePowerStateLocked();
+ }
+ }
+ }
+ }
+
+ @Override // Binder call
+ public void setKeyboardLight(boolean on, int key) {
+ if (key == 1) {
+ if (on)
+ mCapsLight.setColor(0x00ffffff);
+ else
+ mCapsLight.turnOff();
+ } else if (key == 2) {
+ if (on)
+ mFnLight.setColor(0x00ffffff);
+ else
+ mFnLight.turnOff();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ private void wakeUp(final long eventTime, final String reason, final String opPackageName,
+ boolean checkProximity) {
if (eventTime > SystemClock.uptimeMillis()) {
throw new IllegalArgumentException("event time must not be in the future");
}
@@ -3174,14 +3378,86 @@ public final class PowerManagerService extends SystemService
android.Manifest.permission.DEVICE_POWER, null);
final int uid = Binder.getCallingUid();
- final long ident = Binder.clearCallingIdentity();
- try {
- wakeUpInternal(eventTime, reason, uid, opPackageName, uid);
- } finally {
- Binder.restoreCallingIdentity(ident);
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ wakeUpInternal(eventTime, reason, uid, opPackageName, uid);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ };
+ if (checkProximity) {
+ runWithProximityCheck(r);
+ } else {
+ r.run();
+ }
+ }
+
+ private void runWithProximityCheck(Runnable r) {
+ if (mHandler.hasMessages(MSG_WAKE_UP)) {
+ // There is already a message queued;
+ return;
+ }
+
+ TelephonyManager tm = (TelephonyManager)mContext.getSystemService(
+ Context.TELEPHONY_SERVICE);
+ boolean hasIncomingCall = tm.getCallState() == TelephonyManager.CALL_STATE_RINGING;
+
+ if (mProximityWakeSupported && mProximityWakeEnabled && mProximitySensor != null
+ && !hasIncomingCall) {
+ Message msg = mHandler.obtainMessage(MSG_WAKE_UP);
+ msg.obj = r;
+ mHandler.sendMessageDelayed(msg, mProximityTimeOut);
+ runPostProximityCheck(r);
+ } else {
+ r.run();
}
}
+ private void runPostProximityCheck(final Runnable r) {
+ if (mSensorManager == null) {
+ r.run();
+ return;
+ }
+ synchronized (mProximityWakeLock) {
+ mProximityWakeLock.acquire();
+ mProximityListener = new SensorEventListener() {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ cleanupProximityLocked();
+ if (!mHandler.hasMessages(MSG_WAKE_UP)) {
+ Slog.w(TAG, "The proximity sensor took too long, wake event already triggered!");
+ return;
+ }
+ mHandler.removeMessages(MSG_WAKE_UP);
+ float distance = event.values[0];
+ if (distance >= PROXIMITY_NEAR_THRESHOLD ||
+ distance >= mProximitySensor.getMaximumRange()) {
+ r.run();
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {}
+ };
+ mSensorManager.registerListener(mProximityListener,
+ mProximitySensor, SensorManager.SENSOR_DELAY_FASTEST);
+ }
+ }
+
+ @Override // Binder call
+ public void wakeUpWithProximityCheck(long eventTime, String reason, String opPackageName) {
+ wakeUp(eventTime, reason, opPackageName, true);
+ }
+
+ @Override // Binder call
+ public void wakeUp(long eventTime, String reason, String opPackageName) {
+ wakeUp(eventTime, reason, opPackageName, false);
+ }
+
@Override // Binder call
public void goToSleep(long eventTime, int reason, int flags) {
if (eventTime > SystemClock.uptimeMillis()) {
@@ -3444,6 +3720,11 @@ public final class PowerManagerService extends SystemService
}
@Override // Binder call
+ public void cpuBoost(int duration) {
+ mPerf.cpuBoost(duration);
+ }
+
+ @Override // Binder call
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -3499,6 +3780,16 @@ public final class PowerManagerService extends SystemService
}
}
+ private void setButtonBrightnessOverrideFromWindowManagerInternal(int brightness) {
+ synchronized (mLock) {
+ if (mButtonBrightnessOverrideFromWindowManager != brightness) {
+ mButtonBrightnessOverrideFromWindowManager = brightness;
+ mDirty |= DIRTY_SETTINGS;
+ updatePowerStateLocked();
+ }
+ }
+ }
+
private final class LocalService extends PowerManagerInternal {
@Override
public void setScreenBrightnessOverrideFromWindowManager(int screenBrightness) {
@@ -3511,8 +3802,15 @@ public final class PowerManagerService extends SystemService
@Override
public void setButtonBrightnessOverrideFromWindowManager(int screenBrightness) {
- // Do nothing.
- // Button lights are not currently supported in the new implementation.
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ setButtonBrightnessOverrideFromWindowManagerInternal(screenBrightness);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+
}
@Override
@@ -3583,6 +3881,26 @@ public final class PowerManagerService extends SystemService
public void uidGone(int uid) {
uidGoneInternal(uid);
}
+
+ @Override
+ public void powerHint(int hintId, int data) {
+ powerHintInternal(hintId, data);
+ }
+
+ @Override
+ public boolean setPowerSaveMode(boolean mode) {
+ return setLowPowerModeInternal(mode);
+ }
+
+ @Override
+ public int getFeature(int featureId) {
+ return nativeGetFeature(featureId);
+ }
+
+ @Override
+ public void setFeature(int featureId, int data) {
+ nativeSetFeature(featureId, data);
+ }
}
private boolean updateBlockedWakelock(WakeLock wakeLock, boolean update) {
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index d5ad30c..5185747 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2013 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.
@@ -21,9 +22,11 @@ import android.app.ActivityManagerNative;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.IActivityManager;
+import android.app.KeyguardManager;
import android.app.ProgressDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.IBluetoothManager;
+import android.content.pm.ThemeUtils;
import android.media.AudioAttributes;
import android.nfc.NfcAdapter;
import android.nfc.INfcAdapter;
@@ -32,7 +35,11 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnCompletionListener;
import android.os.Handler;
+import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -44,13 +51,16 @@ import android.os.Vibrator;
import android.os.SystemVibrator;
import android.os.storage.IMountService;
import android.os.storage.IMountShutdownObserver;
+import android.provider.Settings;
import android.system.ErrnoException;
import android.system.Os;
+import android.widget.ListView;
import com.android.internal.telephony.ITelephony;
import com.android.server.pm.PackageManagerService;
-
+import com.android.server.power.PowerManagerService;
import android.util.Log;
+import android.view.IWindowManager;
import android.view.WindowManager;
import java.lang.reflect.Method;
import dalvik.system.PathClassLoader;
@@ -58,7 +68,11 @@ import dalvik.system.PathClassLoader;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.OutputStreamWriter;
public final class ShutdownThread extends Thread {
// constants
@@ -76,6 +90,8 @@ public final class ShutdownThread extends Thread {
private static final int RADIO_STOP_PERCENT = 18;
private static final int MOUNT_SERVICE_STOP_PERCENT = 20;
+ private static final String SOFT_REBOOT = "soft_reboot";
+
// length of vibration before shutting down
private static final int SHUTDOWN_VIBRATE_MS = 500;
@@ -113,10 +129,20 @@ public final class ShutdownThread extends Thread {
private PowerManager.WakeLock mCpuWakeLock;
private PowerManager.WakeLock mScreenWakeLock;
private Handler mHandler;
+ private static MediaPlayer mMediaPlayer;
+ private static final String OEM_BOOTANIMATION_FILE = "/oem/media/shutdownanimation.zip";
+ private static final String SYSTEM_BOOTANIMATION_FILE = "/system/media/shutdownanimation.zip";
+ private static final String SYSTEM_ENCRYPTED_BOOTANIMATION_FILE = "/system/media/shutdownanimation-encrypted.zip";
+
+ private static final String SHUTDOWN_MUSIC_FILE = "/system/media/shutdown.wav";
+ private static final String OEM_SHUTDOWN_MUSIC_FILE = "/oem/media/shutdown.wav";
+
+ private boolean isShutdownMusicPlaying = false;
private static AlertDialog sConfirmDialog;
private ProgressDialog mProgressDialog;
+ private static AudioManager mAudioManager;
private ShutdownThread() {
}
@@ -129,9 +155,20 @@ public final class ShutdownThread extends Thread {
* @param confirm true if user confirmation is needed before shutting down.
*/
public static void shutdown(final Context context, boolean confirm) {
+ final Context uiContext = getUiContext(context);
mReboot = false;
mRebootSafeMode = false;
- shutdownInner(context, confirm);
+ shutdownInner(uiContext, confirm);
+ }
+
+ private static boolean isAdvancedRebootPossible(final Context context) {
+ KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
+ boolean keyguardLocked = km.inKeyguardRestrictedInputMode() && km.isKeyguardSecure();
+ boolean advancedRebootEnabled = Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.ADVANCED_REBOOT, 0) == 1;
+ boolean isPrimaryUser = UserHandle.getCallingUserId() == UserHandle.USER_OWNER;
+
+ return advancedRebootEnabled && !keyguardLocked && isPrimaryUser;
}
static void shutdownInner(final Context context, boolean confirm) {
@@ -145,10 +182,19 @@ public final class ShutdownThread extends Thread {
}
boolean showRebootOption = false;
- String[] defaultActions = context.getResources().getStringArray(
- com.android.internal.R.array.config_globalActionsList);
- for (int i = 0; i < defaultActions.length; i++) {
- if (defaultActions[i].equals("reboot")) {
+
+ String[] actionsArray;
+ String actions = Settings.Secure.getStringForUser(context.getContentResolver(),
+ Settings.Secure.POWER_MENU_ACTIONS, UserHandle.USER_CURRENT);
+ if (actions == null) {
+ actionsArray = context.getResources().getStringArray(
+ com.android.internal.R.array.config_globalActionsList);
+ } else {
+ actionsArray = actions.split("\\|");
+ }
+
+ for (int i = 0; i < actionsArray.length; i++) {
+ if (actionsArray[i].equals("reboot")) {
showRebootOption = true;
break;
}
@@ -168,23 +214,57 @@ public final class ShutdownThread extends Thread {
if (confirm) {
final CloseDialogReceiver closer = new CloseDialogReceiver(context);
+ final boolean advancedReboot = isAdvancedRebootPossible(context);
+ final Context uiContext = getUiContext(context);
+
if (sConfirmDialog != null) {
sConfirmDialog.dismiss();
+ sConfirmDialog = null;
}
- sConfirmDialog = new AlertDialog.Builder(context)
+ AlertDialog.Builder confirmDialogBuilder = new AlertDialog.Builder(uiContext)
.setTitle(mRebootSafeMode
? com.android.internal.R.string.reboot_safemode_title
: showRebootOption
? com.android.internal.R.string.reboot_title
- : com.android.internal.R.string.power_off)
- .setMessage(resourceId)
- .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
+ : com.android.internal.R.string.power_off);
+
+ if (!advancedReboot || mRebootSafeMode) {
+ confirmDialogBuilder.setMessage(resourceId);
+ } else {
+ confirmDialogBuilder
+ .setSingleChoiceItems(com.android.internal.R.array.shutdown_reboot_options,
+ 0, null);
+ }
+
+ confirmDialogBuilder.setPositiveButton(com.android.internal.R.string.yes,
+ new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog, int which) {
+ if (!mRebootSafeMode && advancedReboot) {
+ boolean softReboot = false;
+ ListView reasonsList = ((AlertDialog)dialog).getListView();
+ int selected = reasonsList.getCheckedItemPosition();
+ if (selected != ListView.INVALID_POSITION) {
+ String actions[] = context.getResources().getStringArray(
+ com.android.internal.R.array.shutdown_reboot_actions);
+ if (selected >= 0 && selected < actions.length) {
+ mRebootReason = actions[selected];
+ if (actions[selected].equals(SOFT_REBOOT)) {
+ doSoftReboot();
+ return;
+ }
+ }
+ }
+
+ mReboot = true;
+ }
beginShutdownSequence(context);
- }
- })
- .setNegativeButton(com.android.internal.R.string.no, null)
- .create();
+ }
+ });
+
+ confirmDialogBuilder.setNegativeButton(com.android.internal.R.string.no, null);
+ sConfirmDialog = confirmDialogBuilder.create();
+
closer.dialog = sConfirmDialog;
sConfirmDialog.setOnDismissListener(closer);
sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
@@ -194,6 +274,18 @@ public final class ShutdownThread extends Thread {
}
}
+ private static void doSoftReboot() {
+ try {
+ final IActivityManager am =
+ ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
+ if (am != null) {
+ am.restart();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "failure trying to perform soft reboot", e);
+ }
+ }
+
private static class CloseDialogReceiver extends BroadcastReceiver
implements DialogInterface.OnDismissListener {
private Context mContext;
@@ -225,13 +317,35 @@ public final class ShutdownThread extends Thread {
* @param confirm true if user confirmation is needed before shutting down.
*/
public static void reboot(final Context context, String reason, boolean confirm) {
+ final Context uiContext = getUiContext(context);
mReboot = true;
mRebootSafeMode = false;
mRebootUpdate = false;
mRebootReason = reason;
- shutdownInner(context, confirm);
+ shutdownInner(uiContext, confirm);
+ }
+
+ private static String getShutdownMusicFilePath() {
+ final String[] fileName = {OEM_SHUTDOWN_MUSIC_FILE, SHUTDOWN_MUSIC_FILE};
+ File checkFile = null;
+ for(String music : fileName) {
+ checkFile = new File(music);
+ if (checkFile.exists()) {
+ return music;
+ }
+ }
+ return null;
}
+ private static void lockDevice() {
+ IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager
+ .getService(Context.WINDOW_SERVICE));
+ try {
+ wm.updateRotation(false, false);
+ } catch (RemoteException e) {
+ Log.w(TAG, "boot animation can not lock device!");
+ }
+ }
/**
* Request a reboot into safe mode. Must be called from a Looper thread in which its UI
* is shown.
@@ -295,14 +409,32 @@ public final class ShutdownThread extends Thread {
pd.setIndeterminate(true);
}
} else {
+ if (mReboot) {
+ pd.setTitle(context.getText(com.android.internal.R.string.reboot_title));
+ pd.setMessage(context.getText(com.android.internal.R.string.reboot_progress));
+ } else {
+ pd.setTitle(context.getText(com.android.internal.R.string.power_off));
+ pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
+ }
+
pd.setTitle(context.getText(com.android.internal.R.string.power_off));
pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
pd.setIndeterminate(true);
}
- pd.setCancelable(false);
- pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- pd.show();
+ //acquire audio focus to make the other apps to stop playing muisc
+ mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ mAudioManager.requestAudioFocus(null,
+ AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
+
+ if (!checkAnimationFileExist()) {
+ // throw up an indeterminate system dialog to indicate radio is
+ // shutting down.
+ pd.setCancelable(false);
+ pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+
+ pd.show();
+ }
sInstance.mProgressDialog = pd;
sInstance.mContext = context;
@@ -433,6 +565,40 @@ public final class ShutdownThread extends Thread {
sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
}
+ String shutDownFile = null;
+
+ //showShutdownAnimation() is called from here to sync
+ //music and animation properly
+ if(checkAnimationFileExist()) {
+ lockDevice();
+ showShutdownAnimation();
+
+ if (!isSilentMode()
+ && (shutDownFile = getShutdownMusicFilePath()) != null) {
+ isShutdownMusicPlaying = true;
+ shutdownMusicHandler.obtainMessage(0, shutDownFile).sendToTarget();
+ }
+ }
+
+ Log.i(TAG, "wait for shutdown music");
+ final long endTimeForMusic = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
+ synchronized (mActionDoneSync) {
+ while (isShutdownMusicPlaying) {
+ long delay = endTimeForMusic - SystemClock.elapsedRealtime();
+ if (delay <= 0) {
+ Log.w(TAG, "play shutdown music timeout!");
+ break;
+ }
+ try {
+ mActionDoneSync.wait(delay);
+ } catch (InterruptedException e) {
+ }
+ }
+ if (!isShutdownMusicPlaying) {
+ Log.i(TAG, "play shutdown music complete.");
+ }
+ }
+
// Shutdown radios.
shutdownRadios(MAX_RADIO_WAIT_TIME);
if (mRebootUpdate) {
@@ -523,11 +689,10 @@ public final class ShutdownThread extends Thread {
ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
final IBluetoothManager bluetooth =
IBluetoothManager.Stub.asInterface(ServiceManager.checkService(
- BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE));
+ BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE));
try {
- nfcOff = nfc == null ||
- nfc.getState() == NfcAdapter.STATE_OFF;
+ nfcOff = nfc == null || nfc.getState() == NfcAdapter.STATE_OFF;
if (!nfcOff) {
Log.w(TAG, "Turning off NFC...");
nfc.disable(false); // Don't persist new state
@@ -760,4 +925,65 @@ public final class ShutdownThread extends Thread {
Log.e(TAG, "Unknown exception while trying to invoke rebootOrShutdown");
}
}
+
+ private static boolean checkAnimationFileExist() {
+ if (new File(OEM_BOOTANIMATION_FILE).exists()
+ || new File(SYSTEM_BOOTANIMATION_FILE).exists()
+ || new File(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE).exists())
+ return true;
+ else
+ return false;
+ }
+
+ private static boolean isSilentMode() {
+ return mAudioManager.isSilentMode();
+ }
+
+ private static void showShutdownAnimation() {
+ /*
+ * When boot completed, "service.bootanim.exit" property is set to 1.
+ * Bootanimation checks this property to stop showing the boot animation.
+ * Since we use the same code for shutdown animation, we
+ * need to reset this property to 0. If this is not set to 0 then shutdown
+ * will stop and exit after displaying the first frame of the animation
+ */
+ SystemProperties.set("service.bootanim.exit", "0");
+
+ SystemProperties.set("ctl.start", "bootanim");
+ }
+
+ private Handler shutdownMusicHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ String path = (String) msg.obj;
+ mMediaPlayer = new MediaPlayer();
+ try
+ {
+ mMediaPlayer.reset();
+ mMediaPlayer.setDataSource(path);
+ mMediaPlayer.prepare();
+ mMediaPlayer.start();
+ mMediaPlayer.setOnCompletionListener(new OnCompletionListener() {
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ synchronized (mActionDoneSync) {
+ isShutdownMusicPlaying = false;
+ mActionDoneSync.notifyAll();
+ }
+ }
+ });
+ } catch (IOException e) {
+ Log.d(TAG, "play shutdown music error:" + e);
+ }
+ }
+ };
+
+ private static Context getUiContext(Context context) {
+ Context uiContext = null;
+ if (context != null) {
+ uiContext = ThemeUtils.createUiContext(context);
+ uiContext.setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar);
+ }
+ return uiContext != null ? uiContext : context;
+ }
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 7784884..ee926a4 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -106,6 +106,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
static final int MAX_WALLPAPER_COMPONENT_LOG_LENGTH = 128;
static final String WALLPAPER = "wallpaper";
static final String WALLPAPER_INFO = "wallpaper_info.xml";
+ static final String KEYGUARD_WALLPAPER = "keyguard_wallpaper";
+ static final String KEYGUARD_WALLPAPER_INFO = "keyguard_wallpaper_info.xml";
/**
* Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks
@@ -116,17 +118,21 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
private class WallpaperObserver extends FileObserver {
final WallpaperData mWallpaper;
+ final KeyguardWallpaperData mKeyguardWallpaper;
final File mWallpaperDir;
final File mWallpaperFile;
final File mWallpaperInfoFile;
+ final File mKeyguardWallpaperFile;
- public WallpaperObserver(WallpaperData wallpaper) {
+ public WallpaperObserver(WallpaperData wallpaper, KeyguardWallpaperData keyguardWallpaper) {
super(getWallpaperDir(wallpaper.userId).getAbsolutePath(),
CLOSE_WRITE | MOVED_TO | DELETE | DELETE_SELF);
mWallpaperDir = getWallpaperDir(wallpaper.userId);
mWallpaper = wallpaper;
mWallpaperFile = new File(mWallpaperDir, WALLPAPER);
mWallpaperInfoFile = new File(mWallpaperDir, WALLPAPER_INFO);
+ mKeyguardWallpaper = keyguardWallpaper;
+ mKeyguardWallpaperFile = new File(mWallpaperDir, KEYGUARD_WALLPAPER);
}
@Override
@@ -145,6 +151,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
Binder.restoreCallingIdentity(origId);
}
if (mWallpaperFile.equals(changedFile)) {
+ // changing the wallpaper means we'll need to back up the new one
+ long origId = Binder.clearCallingIdentity();
+ BackupManager bm = new BackupManager(mContext);
+ bm.dataChanged();
+ Binder.restoreCallingIdentity(origId);
+
notifyCallbacksLocked(mWallpaper);
final boolean written = (event == CLOSE_WRITE || event == MOVED_TO);
if (mWallpaper.wallpaperComponent == null
@@ -157,6 +169,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
false, mWallpaper, null);
saveSettingsLocked(mWallpaper);
}
+ } else if (mKeyguardWallpaperFile.equals(changedFile)) {
+ notifyCallbacksLocked(mKeyguardWallpaper);
+ if (event == CLOSE_WRITE
+ || mKeyguardWallpaper.imageWallpaperPending) {
+ mKeyguardWallpaper.imageWallpaperPending = false;
+ saveSettingsLocked(mKeyguardWallpaper);
+ }
}
}
}
@@ -176,6 +195,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
final ComponentName mImageWallpaper;
SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
+ SparseArray<KeyguardWallpaperData> mKeyguardWallpaperMap
+ = new SparseArray<KeyguardWallpaperData>();
int mCurrentUserId;
@@ -227,6 +248,37 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
}
}
+ static class KeyguardWallpaperData {
+
+ int userId;
+
+ File wallpaperFile;
+
+ /**
+ * Client is currently writing a new image wallpaper.
+ */
+ boolean imageWallpaperPending;
+
+ /**
+ * Resource name if using a picture from the wallpaper gallery
+ */
+ String name = "";
+
+ /**
+ * List of callbacks registered they should each be notified when the wallpaper is changed.
+ */
+ private RemoteCallbackList<IWallpaperManagerCallback> callbacks
+ = new RemoteCallbackList<IWallpaperManagerCallback>();
+
+ int width = -1;
+ int height = -1;
+
+ KeyguardWallpaperData(int userId) {
+ this.userId = userId;
+ wallpaperFile = new File(getWallpaperDir(userId), KEYGUARD_WALLPAPER);
+ }
+ }
+
class WallpaperConnection extends IWallpaperConnection.Stub
implements ServiceConnection {
final WallpaperInfo mInfo;
@@ -486,6 +538,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
mMonitor.register(context, null, UserHandle.ALL, true);
getWallpaperDir(UserHandle.USER_OWNER).mkdirs();
loadSettingsLocked(UserHandle.USER_OWNER);
+ loadKeyguardSettingsLocked(UserHandle.USER_OWNER);
}
private static File getWallpaperDir(int userId) {
@@ -504,8 +557,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
public void systemRunning() {
if (DEBUG) Slog.v(TAG, "systemReady");
WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_OWNER);
+ KeyguardWallpaperData keyguardWallpaper = mKeyguardWallpaperMap.get(UserHandle.USER_OWNER);
switchWallpaper(wallpaper, null);
- wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
+ wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper, keyguardWallpaper);
wallpaper.wallpaperObserver.startWatching();
IntentFilter userFilter = new IntentFilter();
@@ -572,6 +626,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
wallpaper.wallpaperObserver = null;
}
mWallpaperMap.remove(userId);
+ mKeyguardWallpaperMap.remove(userId);
}
}
}
@@ -584,6 +639,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
wallpaperFile.delete();
File wallpaperInfoFile = new File(getWallpaperDir(userId), WALLPAPER_INFO);
wallpaperInfoFile.delete();
+ File keyguardWallpaperFile = new File(getWallpaperDir(userId), KEYGUARD_WALLPAPER);
+ keyguardWallpaperFile.delete();
+ File keyguardWallpaperInfoFile = new File(getWallpaperDir(userId),
+ KEYGUARD_WALLPAPER_INFO);
+ keyguardWallpaperInfoFile.delete();
}
}
@@ -591,9 +651,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
synchronized (mLock) {
mCurrentUserId = userId;
WallpaperData wallpaper = getWallpaperSafeLocked(userId);
+ KeyguardWallpaperData keyguardWallpaper = getKeyguardWallpaperSafeLocked(userId);
// Not started watching yet, in case wallpaper data was loaded for other reasons.
if (wallpaper.wallpaperObserver == null) {
- wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
+ wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper, keyguardWallpaper);
wallpaper.wallpaperObserver.startWatching();
}
switchWallpaper(wallpaper, reply);
@@ -669,6 +730,40 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
}
}
+ /**
+ * @hide
+ */
+ public void clearKeyguardWallpaper() {
+ if (DEBUG) Slog.v(TAG, "clearWallpaper");
+ synchronized (mLock) {
+ clearKeyguardWallpaperLocked(UserHandle.getCallingUserId(), null);
+ }
+ }
+
+ void clearKeyguardWallpaperLocked(int userId, IRemoteCallback reply) {
+ KeyguardWallpaperData wallpaper = mKeyguardWallpaperMap.get(userId);
+ final long ident = Binder.clearCallingIdentity();
+ wallpaper.imageWallpaperPending = false;
+ wallpaper.height = -1;
+ wallpaper.width = -1;
+ wallpaper.name = "";
+
+ File f = new File(getWallpaperDir(userId), KEYGUARD_WALLPAPER);
+ if (f.exists()) {
+ f.delete();
+ }
+ if (userId != mCurrentUserId)
+ return;
+ Binder.restoreCallingIdentity(ident);
+
+ if (reply != null) {
+ try {
+ reply.sendResult(null);
+ } catch (RemoteException e1) {
+ }
+ }
+ }
+
public boolean hasNamedWallpaper(String name) {
synchronized (mLock) {
List<UserInfo> users;
@@ -837,6 +932,31 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
}
}
+ /** @hide */
+ public ParcelFileDescriptor getKeyguardWallpaper(IWallpaperManagerCallback cb,
+ Bundle outParams) {
+ synchronized (mLock) {
+ int wallpaperUserId = mCurrentUserId;
+ KeyguardWallpaperData wallpaper = mKeyguardWallpaperMap.get(wallpaperUserId);
+ try {
+ if (outParams != null) {
+ outParams.putInt("width", wallpaper.width);
+ outParams.putInt("height", wallpaper.height);
+ }
+ wallpaper.callbacks.register(cb);
+ File f = new File(getWallpaperDir(wallpaperUserId), KEYGUARD_WALLPAPER);
+ if (!f.exists()) {
+ return null;
+ }
+ return ParcelFileDescriptor.open(f, MODE_READ_ONLY);
+ } catch (FileNotFoundException e) {
+ /* Shouldn't happen as we check to see if the file exists */
+ Slog.w(TAG, "Error getting wallpaper", e);
+ }
+ return null;
+ }
+ }
+
public WallpaperInfo getWallpaperInfo() {
int userId = UserHandle.getCallingUserId();
synchronized (mLock) {
@@ -848,6 +968,15 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
}
}
+ /** @hide */
+ public boolean isKeyguardWallpaperSet() {
+ int userId = UserHandle.getCallingUserId();
+ synchronized (mLock) {
+ KeyguardWallpaperData data = mKeyguardWallpaperMap.get(userId);
+ return data.wallpaperFile.exists();
+ }
+ }
+
public ParcelFileDescriptor setWallpaper(String name, String callingPackage) {
checkPermission(android.Manifest.permission.SET_WALLPAPER);
if (!isWallpaperSupported(callingPackage)) {
@@ -901,6 +1030,58 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
}
}
+ public ParcelFileDescriptor setKeyguardWallpaper(String name, String callingPackage) {
+ checkPermission(android.Manifest.permission.SET_KEYGUARD_WALLPAPER);
+ if (!isWallpaperSupported(callingPackage)) {
+ return null;
+ }
+ synchronized (mLock) {
+ if (DEBUG) Slog.v(TAG, "setKeyguardWallpaper");
+ int userId = UserHandle.getCallingUserId();
+ KeyguardWallpaperData wallpaper = mKeyguardWallpaperMap.get(userId);
+ if (wallpaper == null) {
+ throw new IllegalStateException("Keyguard wallpaper not yet initialized for user "
+ + userId);
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ ParcelFileDescriptor pfd = updateKeyguardWallpaperBitmapLocked(name, wallpaper);
+ if (pfd != null) {
+ wallpaper.imageWallpaperPending = true;
+ }
+ return pfd;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ public ParcelFileDescriptor updateKeyguardWallpaperBitmapLocked(String name,
+ KeyguardWallpaperData wallpaper) {
+ if (name == null) name = "";
+ try {
+ File dir = getWallpaperDir(wallpaper.userId);
+ if (!dir.exists()) {
+ dir.mkdir();
+ FileUtils.setPermissions(
+ dir.getPath(),
+ FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
+ -1, -1);
+ }
+ File file = new File(dir, KEYGUARD_WALLPAPER);
+ ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
+ MODE_CREATE|MODE_READ_WRITE|MODE_TRUNCATE);
+ if (!SELinux.restorecon(file)) {
+ return null;
+ }
+ wallpaper.name = name;
+ return fd;
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "Error setting wallpaper", e);
+ }
+ return null;
+ }
+
// ToDo: Remove this version of the function
public void setWallpaperComponent(ComponentName name) {
checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
@@ -1125,6 +1306,22 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
}
+ private void notifyCallbacksLocked(KeyguardWallpaperData wallpaper) {
+ final int n = wallpaper.callbacks.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ try {
+ wallpaper.callbacks.getBroadcastItem(i).onKeyguardWallpaperChanged();
+ } catch (RemoteException e) {
+
+ // The RemoteCallbackList will take care of removing
+ // the dead object for us.
+ }
+ }
+ wallpaper.callbacks.finishBroadcast();
+ final Intent intent = new Intent(Intent.ACTION_KEYGUARD_WALLPAPER_CHANGED);
+ mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
+ }
+
private void checkPermission(String permission) {
if (PackageManager.PERMISSION_GRANTED!= mContext.checkCallingOrSelfPermission(permission)) {
throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
@@ -1142,7 +1339,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
}
private static JournaledFile makeJournaledFile(int userId) {
- final String base = new File(getWallpaperDir(userId), WALLPAPER_INFO).getAbsolutePath();
+ return makeJournaledFile(WALLPAPER_INFO, userId);
+ }
+
+ private static JournaledFile makeJournaledFile(String name, int userId) {
+ final String base = new File(getWallpaperDir(userId), name).getAbsolutePath();
return new JournaledFile(new File(base), new File(base + ".tmp"));
}
@@ -1195,6 +1396,36 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
}
}
+ private void saveSettingsLocked(KeyguardWallpaperData wallpaper) {
+ JournaledFile journal = makeJournaledFile(KEYGUARD_WALLPAPER_INFO, wallpaper.userId);
+ FileOutputStream stream = null;
+ try {
+ stream = new FileOutputStream(journal.chooseForWrite(), false);
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(stream, "utf-8");
+ out.startDocument(null, true);
+
+ out.startTag(null, "kwp");
+ out.attribute(null, "width", Integer.toString(wallpaper.width));
+ out.attribute(null, "height", Integer.toString(wallpaper.height));
+ out.attribute(null, "name", wallpaper.name);
+ out.endTag(null, "kwp");
+
+ out.endDocument();
+ stream.close();
+ journal.commit();
+ } catch (IOException e) {
+ try {
+ if (stream != null) {
+ stream.close();
+ }
+ } catch (IOException ex) {
+ // Ignore
+ }
+ journal.rollback();
+ }
+ }
+
private void migrateFromOld() {
File oldWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY);
File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY);
@@ -1232,6 +1463,16 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
return wallpaper;
}
+ private KeyguardWallpaperData getKeyguardWallpaperSafeLocked(int userId) {
+ KeyguardWallpaperData wallpaper = mKeyguardWallpaperMap.get(userId);
+ if (wallpaper == null) {
+ loadKeyguardSettingsLocked(userId);
+ wallpaper = mKeyguardWallpaperMap.get(userId);
+ }
+ return wallpaper;
+ }
+
+
private void loadSettingsLocked(int userId) {
if (DEBUG) Slog.v(TAG, "loadSettingsLocked");
@@ -1326,6 +1567,80 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
}
}
+ private void loadKeyguardSettingsLocked(int userId) {
+ if (DEBUG) Slog.v(TAG, "loadKeyguardSettingsLocked");
+
+ JournaledFile journal = makeJournaledFile(KEYGUARD_WALLPAPER_INFO, userId);
+ FileInputStream stream = null;
+ File file = journal.chooseForRead();
+ KeyguardWallpaperData keyguardWallpaper = mKeyguardWallpaperMap.get(userId);
+ if (keyguardWallpaper == null) {
+ keyguardWallpaper = new KeyguardWallpaperData(userId);
+ mKeyguardWallpaperMap.put(userId, keyguardWallpaper);
+ }
+ boolean success = false;
+ try {
+ stream = new FileInputStream(file);
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, null);
+
+ int type;
+ do {
+ type = parser.next();
+ if (type == XmlPullParser.START_TAG) {
+ String tag = parser.getName();
+ if ("kwp".equals(tag)) {
+ keyguardWallpaper.width = Integer.parseInt(parser.getAttributeValue(null,
+ "width"));
+ keyguardWallpaper.height = Integer.parseInt(parser
+ .getAttributeValue(null, "height"));
+ keyguardWallpaper.name = parser.getAttributeValue(null, "name");
+ if (DEBUG) {
+ Slog.v(TAG, "mWidth:" + keyguardWallpaper.width);
+ Slog.v(TAG, "mHeight:" + keyguardWallpaper.height);
+ Slog.v(TAG, "mName:" + keyguardWallpaper.name);
+ }
+ }
+ }
+ } while (type != XmlPullParser.END_DOCUMENT);
+ success = true;
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "no current wallpaper -- first boot?");
+ } catch (NullPointerException e) {
+ Slog.w(TAG, "failed parsing " + file + " " + e);
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "failed parsing " + file + " " + e);
+ } catch (XmlPullParserException e) {
+ Slog.w(TAG, "failed parsing " + file + " " + e);
+ } catch (IOException e) {
+ Slog.w(TAG, "failed parsing " + file + " " + e);
+ } catch (IndexOutOfBoundsException e) {
+ Slog.w(TAG, "failed parsing " + file + " " + e);
+ }
+ try {
+ if (stream != null) {
+ stream.close();
+ }
+ } catch (IOException e) {
+ // Ignore
+ }
+
+ if (!success) {
+ keyguardWallpaper.width = -1;
+ keyguardWallpaper.height = -1;
+ keyguardWallpaper.name = "";
+ }
+
+ // We always want to have some reasonable width hint.
+ int baseSize = getMaximumSizeDimension();
+ if (keyguardWallpaper.width < baseSize) {
+ keyguardWallpaper.width = baseSize;
+ }
+ if (keyguardWallpaper.height < baseSize) {
+ keyguardWallpaper.height = baseSize;
+ }
+ }
+
private int getMaximumSizeDimension() {
WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
Display d = wm.getDefaultDisplay();
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index a8ba0f9..f0793b8 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -429,6 +429,34 @@ final class Session extends IWindowSession.Stub
}
}
+ /**
+ * @hide
+ */
+ public int getLastWallpaperX() {
+ synchronized(mService.mWindowMap) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return mService.getLastWallpaperX();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public int getLastWallpaperY() {
+ synchronized(mService.mWindowMap) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return mService.getLastWallpaperY();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
int z, Bundle extras, boolean sync) {
synchronized(mService.mWindowMap) {
diff --git a/services/core/java/com/android/server/wm/Watermark.java b/services/core/java/com/android/server/wm/Watermark.java
index e226e3d..3ae783e 100644
--- a/services/core/java/com/android/server/wm/Watermark.java
+++ b/services/core/java/com/android/server/wm/Watermark.java
@@ -23,6 +23,7 @@ import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.Paint.FontMetricsInt;
+import android.os.SystemProperties;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
@@ -76,6 +77,14 @@ class Watermark {
else c2 -= '0';
builder.append((char)(255-((c1*16)+c2)));
}
+
+ int appendDisplayVersion = (WindowManagerService.getPropertyInt(tokens, 10,
+ TypedValue.COMPLEX_UNIT_PX, 0, dm));
+ if (appendDisplayVersion != 0) {
+ builder.append(" - ");
+ builder.append(SystemProperties.get("ro.cm.display.version"));
+ }
+
mText = builder.toString();
if (false) {
Log.i(WindowManagerService.TAG, "Final text: " + mText);
@@ -149,27 +158,32 @@ class Watermark {
if (c != null) {
c.drawColor(0, PorterDuff.Mode.CLEAR);
- int deltaX = mDeltaX;
- int deltaY = mDeltaY;
-
- // deltaX shouldn't be close to a round fraction of our
- // x step, or else things will line up too much.
- int div = (dw+mTextWidth)/deltaX;
- int rem = (dw+mTextWidth) - (div*deltaX);
- int qdelta = deltaX/4;
- if (rem < qdelta || rem > (deltaX-qdelta)) {
- deltaX += deltaX/3;
- }
+ if (mDeltaX != 0 || mDeltaY != 0) {
+ int deltaX = mDeltaX;
+ int deltaY = mDeltaY;
+
+ // deltaX shouldn't be close to a round fraction of our
+ // x step, or else things will line up too much.
+ int div = (dw+mTextWidth)/deltaX;
+ int rem = (dw+mTextWidth) - (div*deltaX);
+ int qdelta = deltaX/4;
+ if (rem < qdelta || rem > (deltaX-qdelta)) {
+ deltaX += deltaX/3;
+ }
- int y = -mTextHeight;
- int x = -mTextWidth;
- while (y < (dh+mTextHeight)) {
- c.drawText(mText, x, y, mTextPaint);
- x += deltaX;
- if (x >= dw) {
- x -= (dw+mTextWidth);
- y += deltaY;
+ int y = -mTextHeight;
+ int x = -mTextWidth;
+ while (y < (dh+mTextHeight)) {
+ c.drawText(mText, x, y, mTextPaint);
+ x += deltaX;
+ if (x >= dw) {
+ x -= (dw+mTextWidth);
+ y += deltaY;
+ }
}
+ } else {
+ c.drawText(mText, dw - mTextWidth,
+ dh - mTextHeight*4, mTextPaint);
}
mSurface.unlockCanvasAndPost(c);
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 85a9624..c5cd729 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -92,6 +92,7 @@ public class WindowAnimator {
boolean mKeyguardGoingAway;
boolean mKeyguardGoingAwayToNotificationShade;
boolean mKeyguardGoingAwayDisableWindowAnimations;
+ boolean mKeyguardGoingAwayShowingMedia;
/** Use one animation for all entering activities after keyguard is dismissed. */
Animation mPostKeyguardExitAnimation;
@@ -486,7 +487,7 @@ public class WindowAnimator {
&& !mKeyguardGoingAwayDisableWindowAnimations) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "updateWindowsLocked: wallpaper animating away");
Animation a = mPolicy.createForceHideWallpaperExitAnimation(
- mKeyguardGoingAwayToNotificationShade);
+ mKeyguardGoingAwayToNotificationShade, mKeyguardGoingAwayShowingMedia);
if (a != null) {
wallpaper.mWinAnimator.setAnimation(a);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8a0a7c8..867292a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -298,6 +298,7 @@ public class WindowManagerService extends IWindowManager.Stub
private static final String SYSTEM_SECURE = "ro.secure";
private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
+ private static final String PERSIST_SYS_LCD_DENSITY = "persist.sys.lcd_density";
private static final String DENSITY_OVERRIDE = "ro.config.density_override";
private static final String SIZE_OVERRIDE = "ro.config.size_override";
@@ -2145,6 +2146,36 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ public int getLastWallpaperX() {
+ int curTokenIndex = mWallpaperTokens.size();
+ while (curTokenIndex > 0) {
+ curTokenIndex--;
+ WindowToken token = mWallpaperTokens.get(curTokenIndex);
+ int curWallpaperIndex = token.windows.size();
+ while (curWallpaperIndex > 0) {
+ curWallpaperIndex--;
+ WindowState wallpaperWin = token.windows.get(curWallpaperIndex);
+ return wallpaperWin.mXOffset;
+ }
+ }
+ return -1;
+ }
+
+ public int getLastWallpaperY() {
+ int curTokenIndex = mWallpaperTokens.size();
+ while (curTokenIndex > 0) {
+ curTokenIndex--;
+ WindowToken token = mWallpaperTokens.get(curTokenIndex);
+ int curWallpaperIndex = token.windows.size();
+ while (curWallpaperIndex > 0) {
+ curWallpaperIndex--;
+ WindowState wallpaperWin = token.windows.get(curWallpaperIndex);
+ return wallpaperWin.mYOffset;
+ }
+ }
+ return -1;
+ }
+
boolean updateWallpaperOffsetLocked(WindowState wallpaperWin, int dw, int dh,
boolean sync) {
boolean changed = false;
@@ -5466,7 +5497,7 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void keyguardGoingAway(boolean disableWindowAnimations,
- boolean keyguardGoingToNotificationShade) {
+ boolean keyguardGoingToNotificationShade, boolean keyguardShowingMedia) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires DISABLE_KEYGUARD permission");
@@ -5477,6 +5508,7 @@ public class WindowManagerService extends IWindowManager.Stub
mAnimator.mKeyguardGoingAway = true;
mAnimator.mKeyguardGoingAwayToNotificationShade = keyguardGoingToNotificationShade;
mAnimator.mKeyguardGoingAwayDisableWindowAnimations = disableWindowAnimations;
+ mAnimator.mKeyguardGoingAwayShowingMedia = keyguardShowingMedia;
requestTraversalLocked();
}
}
@@ -5777,7 +5809,7 @@ public class WindowManagerService extends IWindowManager.Stub
boolean wallpaperEnabled = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_enableWallpaperService)
&& !mOnlyCore;
- boolean haveKeyguard = true;
+ boolean haveKeyguard = false;
// TODO(multidisplay): Expand to all displays?
final WindowList windows = getDefaultWindowListLocked();
final int N = windows.size();
@@ -8558,6 +8590,9 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public int getInitialDisplayDensity(int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ return DisplayMetrics.DENSITY_DEVICE_DEFAULT;
+ }
synchronized (mWindowMap) {
final DisplayContent displayContent = getDisplayContentLocked(displayId);
if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
@@ -8571,6 +8606,9 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public int getBaseDisplayDensity(int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ return DisplayMetrics.DENSITY_PREFERRED;
+ }
synchronized (mWindowMap) {
final DisplayContent displayContent = getDisplayContentLocked(displayId);
if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
@@ -8598,6 +8636,7 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized(mWindowMap) {
final DisplayContent displayContent = getDisplayContentLocked(displayId);
if (displayContent != null) {
+ SystemProperties.set(PERSIST_SYS_LCD_DENSITY, Integer.toString(density));
setForcedDisplayDensityLocked(displayContent, density);
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.DISPLAY_DENSITY_FORCED, Integer.toString(density));
@@ -8606,6 +8645,10 @@ public class WindowManagerService extends IWindowManager.Stub
} finally {
Binder.restoreCallingIdentity(ident);
}
+ try {
+ ActivityManagerNative.getDefault().restart();
+ } catch (RemoteException e) {
+ }
}
// displayContent must not be null
@@ -8634,6 +8677,7 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized(mWindowMap) {
final DisplayContent displayContent = getDisplayContentLocked(displayId);
if (displayContent != null) {
+ SystemProperties.set(PERSIST_SYS_LCD_DENSITY, null);
setForcedDisplayDensityLocked(displayContent,
displayContent.mInitialDisplayDensity);
Settings.Global.putString(mContext.getContentResolver(),
@@ -8643,6 +8687,10 @@ public class WindowManagerService extends IWindowManager.Stub
} finally {
Binder.restoreCallingIdentity(ident);
}
+ try {
+ ActivityManagerNative.getDefault().restart();
+ } catch (RemoteException e) {
+ }
}
// displayContent must not be null
@@ -11097,6 +11145,16 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
+ public boolean hasPermanentMenuKey() {
+ return mPolicy.hasPermanentMenuKey();
+ }
+
+ @Override
+ public boolean needsNavigationBar() {
+ return mPolicy.needsNavigationBar();
+ }
+
+ @Override
public void lockNow(Bundle options) {
mPolicy.lockNow(options);
}