summaryrefslogtreecommitdiffstats
path: root/services/core/java/com/android
diff options
context:
space:
mode:
Diffstat (limited to 'services/core/java/com/android')
-rw-r--r--services/core/java/com/android/server/AssetAtlasService.java118
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java115
-rw-r--r--services/core/java/com/android/server/MountService.java245
-rw-r--r--services/core/java/com/android/server/Watchdog.java17
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java124
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java13
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java189
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java77
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java22
-rw-r--r--services/core/java/com/android/server/content/SyncManager.java2
-rw-r--r--services/core/java/com/android/server/fingerprint/FingerprintService.java379
-rw-r--r--services/core/java/com/android/server/location/FlpHardwareProvider.java19
-rw-r--r--services/core/java/com/android/server/location/FusedLocationHardwareSecure.java6
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java110
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java29
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java3
-rw-r--r--services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java3
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java7
-rw-r--r--services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java57
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java1
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java2
21 files changed, 1101 insertions, 437 deletions
diff --git a/services/core/java/com/android/server/AssetAtlasService.java b/services/core/java/com/android/server/AssetAtlasService.java
index 66cc29a..26f4232 100644
--- a/services/core/java/com/android/server/AssetAtlasService.java
+++ b/services/core/java/com/android/server/AssetAtlasService.java
@@ -199,11 +199,6 @@ public class AssetAtlasService extends IAssetAtlas.Stub {
private final ArrayList<Bitmap> mBitmaps;
private final int mPixelCount;
- private long mNativeBitmap;
-
- // Used for debugging only
- private Bitmap mAtlasBitmap;
-
Renderer(ArrayList<Bitmap> bitmaps, int pixelCount) {
mBitmaps = bitmaps;
mPixelCount = pixelCount;
@@ -258,8 +253,9 @@ public class AssetAtlasService extends IAssetAtlas.Stub {
// We always render the atlas into a bitmap. This bitmap is then
// uploaded into the GraphicBuffer using OpenGL to swizzle the content
- final Canvas canvas = acquireCanvas(buffer.getWidth(), buffer.getHeight());
- if (canvas == null) return false;
+ final Bitmap atlasBitmap = Bitmap.createBitmap(
+ buffer.getWidth(), buffer.getHeight(), Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(atlasBitmap);
final Atlas.Entry entry = new Atlas.Entry();
@@ -268,108 +264,78 @@ public class AssetAtlasService extends IAssetAtlas.Stub {
int mapIndex = 0;
boolean result = false;
- try {
- final long startRender = System.nanoTime();
- final int count = mBitmaps.size();
-
- for (int i = 0; i < count; i++) {
- final Bitmap bitmap = mBitmaps.get(i);
- if (atlas.pack(bitmap.getWidth(), bitmap.getHeight(), entry) != null) {
- // We have more bitmaps to pack than the current configuration
- // says, we were most likely not able to detect a change in the
- // list of preloaded drawables, abort and delete the configuration
- if (mapIndex >= mAtlasMap.length) {
- deleteDataFile();
- break;
- }
+ final long startRender = System.nanoTime();
+ final int count = mBitmaps.size();
- canvas.save();
- canvas.translate(entry.x, entry.y);
- if (entry.rotated) {
- canvas.translate(bitmap.getHeight(), 0.0f);
- canvas.rotate(90.0f);
- }
- canvas.drawBitmap(bitmap, 0.0f, 0.0f, null);
- canvas.restore();
- atlasMap[mapIndex++] = bitmap.getSkBitmap();
- atlasMap[mapIndex++] = entry.x;
- atlasMap[mapIndex++] = entry.y;
- atlasMap[mapIndex++] = entry.rotated ? 1 : 0;
+ for (int i = 0; i < count; i++) {
+ final Bitmap bitmap = mBitmaps.get(i);
+ if (atlas.pack(bitmap.getWidth(), bitmap.getHeight(), entry) != null) {
+ // We have more bitmaps to pack than the current configuration
+ // says, we were most likely not able to detect a change in the
+ // list of preloaded drawables, abort and delete the configuration
+ if (mapIndex >= mAtlasMap.length) {
+ deleteDataFile();
+ break;
}
- }
- final long endRender = System.nanoTime();
- if (mNativeBitmap != 0) {
- result = nUploadAtlas(buffer, mNativeBitmap);
- }
-
- final long endUpload = System.nanoTime();
- if (DEBUG_ATLAS) {
- float renderDuration = (endRender - startRender) / 1000.0f / 1000.0f;
- float uploadDuration = (endUpload - endRender) / 1000.0f / 1000.0f;
- Log.d(LOG_TAG, String.format("Rendered atlas in %.2fms (%.2f+%.2fms)",
- renderDuration + uploadDuration, renderDuration, uploadDuration));
+ canvas.save();
+ canvas.translate(entry.x, entry.y);
+ if (entry.rotated) {
+ canvas.translate(bitmap.getHeight(), 0.0f);
+ canvas.rotate(90.0f);
+ }
+ canvas.drawBitmap(bitmap, 0.0f, 0.0f, null);
+ canvas.restore();
+ atlasMap[mapIndex++] = bitmap.refSkPixelRef();
+ atlasMap[mapIndex++] = entry.x;
+ atlasMap[mapIndex++] = entry.y;
+ atlasMap[mapIndex++] = entry.rotated ? 1 : 0;
}
+ }
- } finally {
- releaseCanvas(canvas);
+ final long endRender = System.nanoTime();
+ releaseCanvas(canvas, atlasBitmap);
+ result = nUploadAtlas(buffer, atlasBitmap);
+ atlasBitmap.recycle();
+ final long endUpload = System.nanoTime();
+
+ if (DEBUG_ATLAS) {
+ float renderDuration = (endRender - startRender) / 1000.0f / 1000.0f;
+ float uploadDuration = (endUpload - endRender) / 1000.0f / 1000.0f;
+ Log.d(LOG_TAG, String.format("Rendered atlas in %.2fms (%.2f+%.2fms)",
+ renderDuration + uploadDuration, renderDuration, uploadDuration));
}
return result;
}
/**
- * Returns a Canvas for the specified buffer. If {@link #DEBUG_ATLAS_TEXTURE}
- * is turned on, the returned Canvas will render into a local bitmap that
- * will then be saved out to disk for debugging purposes.
- * @param width
- * @param height
- */
- private Canvas acquireCanvas(int width, int height) {
- if (DEBUG_ATLAS_TEXTURE) {
- mAtlasBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- return new Canvas(mAtlasBitmap);
- } else {
- Canvas canvas = new Canvas();
- mNativeBitmap = nAcquireAtlasCanvas(canvas, width, height);
- return canvas;
- }
- }
-
- /**
* Releases the canvas used to render into the buffer. Calling this method
* will release any resource previously acquired. If {@link #DEBUG_ATLAS_TEXTURE}
* is turend on, calling this method will write the content of the atlas
* to disk in /data/system/atlas.png for debugging.
*/
- private void releaseCanvas(Canvas canvas) {
+ private void releaseCanvas(Canvas canvas, Bitmap atlasBitmap) {
+ canvas.setBitmap(null);
if (DEBUG_ATLAS_TEXTURE) {
- canvas.setBitmap(null);
File systemDirectory = new File(Environment.getDataDirectory(), "system");
File dataFile = new File(systemDirectory, "atlas.png");
try {
FileOutputStream out = new FileOutputStream(dataFile);
- mAtlasBitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
+ atlasBitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
out.close();
} catch (FileNotFoundException e) {
// Ignore
} catch (IOException e) {
// Ignore
}
-
- mAtlasBitmap.recycle();
- mAtlasBitmap = null;
- } else {
- nReleaseAtlasCanvas(canvas, mNativeBitmap);
}
}
}
- private static native long nAcquireAtlasCanvas(Canvas canvas, int width, int height);
- private static native void nReleaseAtlasCanvas(Canvas canvas, long bitmap);
- private static native boolean nUploadAtlas(GraphicBuffer buffer, long bitmap);
+ private static native boolean nUploadAtlas(GraphicBuffer buffer, Bitmap bitmap);
@Override
public boolean isCompatible(int ppid) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 26362a4..484908d 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -163,6 +163,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
private static final String NETWORK_RESTORE_DELAY_PROP_NAME =
"android.telephony.apn-restore";
+ // How long to wait before putting up a "This network doesn't have an Internet connection,
+ // connect anyway?" dialog after the user selects a network that doesn't validate.
+ private static final int PROMPT_UNVALIDATED_DELAY_MS = 8 * 1000;
+
// How long to delay to removal of a pending intent based request.
// See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
private final int mReleasePendingIntentDelayMs;
@@ -326,6 +330,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
*/
private static final int EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT = 27;
+ /**
+ * used to specify whether a network should be used even if unvalidated.
+ * arg1 = whether to accept the network if it's unvalidated (1 or 0)
+ * arg2 = whether to remember this choice in the future (1 or 0)
+ * obj = network
+ */
+ private static final int EVENT_SET_ACCEPT_UNVALIDATED = 28;
+
+ /**
+ * used to ask the user to confirm a connection to an unvalidated network.
+ * obj = network
+ */
+ private static final int EVENT_PROMPT_UNVALIDATED = 29;
/** Handler used for internal events. */
final private InternalHandler mHandler;
@@ -1870,6 +1887,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
loge("ERROR: created network explicitly selected.");
}
nai.networkMisc.explicitlySelected = true;
+ nai.networkMisc.acceptUnvalidated = (boolean) msg.obj;
break;
}
case NetworkMonitor.EVENT_NETWORK_TESTED: {
@@ -1893,6 +1911,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
android.net.NetworkAgent.CMD_REPORT_NETWORK_STATUS,
(valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
0, null);
+
+ // TODO: trigger a NetworkCapabilities update so that the dialog can know
+ // that the network is now validated and close itself.
}
break;
}
@@ -2256,6 +2277,91 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
+ public void setAcceptUnvalidated(Network network, boolean accept, boolean always) {
+ enforceConnectivityInternalPermission();
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_ACCEPT_UNVALIDATED,
+ accept ? 1 : 0, always ? 1: 0, network));
+ }
+
+ private void handleSetAcceptUnvalidated(Network network, boolean accept, boolean always) {
+ if (DBG) log("handleSetAcceptUnvalidated network=" + network +
+ " accept=" + accept + " always=" + always);
+
+ NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+ if (nai == null) {
+ // Nothing to do.
+ return;
+ }
+
+ if (nai.everValidated) {
+ // The network validated while the dialog box was up. Don't make any changes. There's a
+ // TODO in the dialog code to make it go away if the network validates; once that's
+ // implemented, taking action here will be confusing.
+ return;
+ }
+
+ if (!nai.networkMisc.explicitlySelected) {
+ Slog.wtf(TAG, "BUG: setAcceptUnvalidated non non-explicitly selected network");
+ }
+
+ if (accept != nai.networkMisc.acceptUnvalidated) {
+ int oldScore = nai.getCurrentScore();
+ nai.networkMisc.acceptUnvalidated = accept;
+ rematchAllNetworksAndRequests(nai, oldScore);
+ sendUpdatedScoreToFactories(nai);
+ }
+
+ if (always) {
+ nai.asyncChannel.sendMessage(
+ NetworkAgent.CMD_SAVE_ACCEPT_UNVALIDATED, accept ? 1 : 0);
+ }
+
+ // TODO: should we also disconnect from the network if accept is false?
+ }
+
+ private void scheduleUnvalidatedPrompt(NetworkAgentInfo nai) {
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(EVENT_PROMPT_UNVALIDATED, nai.network),
+ PROMPT_UNVALIDATED_DELAY_MS);
+ }
+
+ private void handlePromptUnvalidated(Network network) {
+ NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+
+ // Only prompt if the network is unvalidated and was explicitly selected by the user, and if
+ // we haven't already been told to switch to it regardless of whether it validated or not.
+ if (nai == null || nai.everValidated ||
+ !nai.networkMisc.explicitlySelected || nai.networkMisc.acceptUnvalidated) {
+ return;
+ }
+
+ // TODO: What should we do if we've already switched to this network because we had no
+ // better option? There are two obvious alternatives.
+ //
+ // 1. Decide that there's no point prompting because this is our only usable network.
+ // However, because we didn't prompt, if later on a validated network comes along, we'll
+ // either a) silently switch to it - bad if the user wanted to connect to stay on this
+ // unvalidated network - or b) prompt the user at that later time - bad because the user
+ // might not understand why they are now being prompted.
+ //
+ // 2. Always prompt the user, even if we have no other network to use. The user could then
+ // try to find an alternative network to join (remember, if we got here, then the user
+ // selected this network manually). This is bad because the prompt isn't really very
+ // useful.
+ //
+ // For now we do #1, but we can revisit that later.
+ if (isDefaultNetwork(nai)) {
+ return;
+ }
+
+ Intent intent = new Intent(ConnectivityManager.ACTION_PROMPT_UNVALIDATED);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK, network);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setClassName("com.android.settings",
+ "com.android.settings.wifi.WifiNoInternetDialog");
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ }
+
private class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
@@ -2326,6 +2432,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
handleReleaseNetworkRequest((NetworkRequest) msg.obj, msg.arg1);
break;
}
+ case EVENT_SET_ACCEPT_UNVALIDATED: {
+ handleSetAcceptUnvalidated((Network) msg.obj, msg.arg1 != 0, msg.arg2 != 0);
+ break;
+ }
+ case EVENT_PROMPT_UNVALIDATED: {
+ handlePromptUnvalidated((Network) msg.obj);
+ break;
+ }
case EVENT_SYSTEM_READY: {
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
nai.networkMonitor.systemReady = true;
@@ -4125,6 +4239,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
notifyIfacesChanged();
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
+ scheduleUnvalidatedPrompt(networkAgent);
if (networkAgent.isVPN()) {
// Temporarily disable the default proxy (not global).
synchronized (mProxyLock) {
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index cac2f5c..4c937f7 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -16,6 +16,13 @@
package com.android.server;
+import static com.android.internal.util.XmlUtils.readIntAttribute;
+import static com.android.internal.util.XmlUtils.readStringAttribute;
+import static com.android.internal.util.XmlUtils.writeIntAttribute;
+import static com.android.internal.util.XmlUtils.writeStringAttribute;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
import android.Manifest;
import android.app.ActivityManagerNative;
import android.app.AppOpsManager;
@@ -55,9 +62,13 @@ import android.os.storage.StorageVolume;
import android.os.storage.VolumeInfo;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.AtomicFile;
+import android.util.DebugUtils;
import android.util.Log;
import android.util.Slog;
+import android.util.Xml;
+import libcore.io.IoUtils;
import libcore.util.EmptyArray;
import libcore.util.HexEncoding;
@@ -66,14 +77,21 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IMediaContainerService;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.NativeDaemonConnector.Command;
import com.android.server.NativeDaemonConnector.SensitiveArg;
import com.android.server.pm.PackageManagerService;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
@@ -214,6 +232,57 @@ class MountService extends IMountService.Stub
public static final int FstrimCompleted = 700;
}
+ private static final String TAG_VOLUMES = "volumes";
+ private static final String TAG_VOLUME = "volume";
+ private static final String ATTR_TYPE = "type";
+ private static final String ATTR_FS_UUID = "fsUuid";
+ private static final String ATTR_NICKNAME = "nickname";
+ private static final String ATTR_USER_FLAGS = "userFlags";
+
+ private final AtomicFile mMetadataFile;
+
+ private static class VolumeMetadata {
+ public final int type;
+ public final String fsUuid;
+ public String nickname;
+ public int userFlags;
+
+ public VolumeMetadata(int type, String fsUuid) {
+ this.type = type;
+ this.fsUuid = Preconditions.checkNotNull(fsUuid);
+ }
+
+ public static VolumeMetadata read(XmlPullParser in) throws IOException {
+ final int type = readIntAttribute(in, ATTR_TYPE);
+ final String fsUuid = readStringAttribute(in, ATTR_FS_UUID);
+ final VolumeMetadata meta = new VolumeMetadata(type, fsUuid);
+ meta.nickname = readStringAttribute(in, ATTR_NICKNAME);
+ meta.userFlags = readIntAttribute(in, ATTR_USER_FLAGS);
+ return meta;
+ }
+
+ public static void write(XmlSerializer out, VolumeMetadata meta) throws IOException {
+ out.startTag(null, TAG_VOLUME);
+ writeIntAttribute(out, ATTR_TYPE, meta.type);
+ writeStringAttribute(out, ATTR_FS_UUID, meta.fsUuid);
+ writeStringAttribute(out, ATTR_NICKNAME, meta.nickname);
+ writeIntAttribute(out, ATTR_USER_FLAGS, meta.userFlags);
+ out.endTag(null, TAG_VOLUME);
+ }
+
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("VolumeMetadata:");
+ pw.increaseIndent();
+ pw.printPair("type", DebugUtils.valueToString(VolumeInfo.class, "TYPE_", type));
+ pw.printPair("fsUuid", fsUuid);
+ pw.printPair("nickname", nickname);
+ pw.printPair("userFlags",
+ DebugUtils.flagsToString(VolumeInfo.class, "USER_FLAG_", userFlags));
+ pw.decreaseIndent();
+ pw.println();
+ }
+ }
+
/**
* <em>Never</em> hold the lock while performing downcalls into vold, since
* unsolicited events can suddenly appear to update data structures.
@@ -222,11 +291,18 @@ class MountService extends IMountService.Stub
@GuardedBy("mLock")
private int[] mStartedUsers = EmptyArray.INT;
+
+ /** Map from disk ID to disk */
@GuardedBy("mLock")
private ArrayMap<String, DiskInfo> mDisks = new ArrayMap<>();
+ /** Map from volume ID to disk */
@GuardedBy("mLock")
private ArrayMap<String, VolumeInfo> mVolumes = new ArrayMap<>();
+ /** Map from UUID to metadata */
+ @GuardedBy("mLock")
+ private ArrayMap<String, VolumeMetadata> mMetadata = new ArrayMap<>();
+
private DiskInfo findDiskById(String id) {
synchronized (mLock) {
final DiskInfo disk = mDisks.get(id);
@@ -260,6 +336,15 @@ class MountService extends IMountService.Stub
throw new IllegalArgumentException("No volume found for path " + path);
}
+ private VolumeMetadata findOrCreateMetadataLocked(VolumeInfo vol) {
+ VolumeMetadata meta = mMetadata.get(vol.fsUuid);
+ if (meta == null) {
+ meta = new VolumeMetadata(vol.type, vol.fsUuid);
+ mMetadata.put(meta.fsUuid, meta);
+ }
+ return meta;
+ }
+
private static int sNextMtpIndex = 1;
private static int allocateMtpIndex(String volId) {
@@ -740,19 +825,29 @@ class MountService extends IMountService.Stub
}
case VoldResponseCode.DISK_VOLUME_CREATED: {
if (cooked.length != 3) break;
- final DiskInfo disk = mDisks.get(cooked[1]);
+ final String diskId = cooked[1];
final String volId = cooked[2];
+ final DiskInfo disk = mDisks.get(diskId);
if (disk != null) {
- disk.volumes = ArrayUtils.appendElement(String.class, disk.volumes, volId);
+ disk.volumeIds = ArrayUtils.appendElement(String.class, disk.volumeIds, volId);
+ }
+ final VolumeInfo vol = mVolumes.get(volId);
+ if (vol != null) {
+ vol.diskId = diskId;
}
break;
}
case VoldResponseCode.DISK_VOLUME_DESTROYED: {
if (cooked.length != 3) break;
- final DiskInfo disk = mDisks.get(cooked[1]);
+ final String diskId = cooked[1];
final String volId = cooked[2];
+ final DiskInfo disk = mDisks.get(diskId);
if (disk != null) {
- disk.volumes = ArrayUtils.removeElement(String.class, disk.volumes, volId);
+ disk.volumeIds = ArrayUtils.removeElement(String.class, disk.volumeIds, volId);
+ }
+ final VolumeInfo vol = mVolumes.get(volId);
+ if (vol != null) {
+ vol.diskId = null;
}
break;
}
@@ -789,6 +884,7 @@ class MountService extends IMountService.Stub
if (vol != null) {
vol.fsType = cooked[2];
}
+ mCallbacks.notifyVolumeMetadataChanged(vol.clone());
break;
}
case VoldResponseCode.VOLUME_FS_UUID_CHANGED: {
@@ -797,6 +893,8 @@ class MountService extends IMountService.Stub
if (vol != null) {
vol.fsUuid = cooked[2];
}
+ refreshMetadataLocked();
+ mCallbacks.notifyVolumeMetadataChanged(vol.clone());
break;
}
case VoldResponseCode.VOLUME_FS_LABEL_CHANGED: {
@@ -805,6 +903,7 @@ class MountService extends IMountService.Stub
if (vol != null) {
vol.fsLabel = cooked[2];
}
+ mCallbacks.notifyVolumeMetadataChanged(vol.clone());
break;
}
case VoldResponseCode.VOLUME_PATH_CHANGED: {
@@ -891,6 +990,25 @@ class MountService extends IMountService.Stub
}
}
+ /**
+ * Refresh latest metadata into any currently active {@link VolumeInfo}.
+ */
+ private void refreshMetadataLocked() {
+ final int size = mVolumes.size();
+ for (int i = 0; i < size; i++) {
+ final VolumeInfo vol = mVolumes.valueAt(i);
+ final VolumeMetadata meta = mMetadata.get(vol.fsUuid);
+
+ if (meta != null) {
+ vol.nickname = meta.nickname;
+ vol.userFlags = meta.userFlags;
+ } else {
+ vol.nickname = null;
+ vol.userFlags = 0;
+ }
+ }
+ }
+
private void enforcePermission(String perm) {
mContext.enforceCallingOrSelfPermission(perm, perm);
}
@@ -939,6 +1057,13 @@ class MountService extends IMountService.Stub
mLastMaintenance = mLastMaintenanceFile.lastModified();
}
+ mMetadataFile = new AtomicFile(
+ new File(Environment.getSystemSecureDirectory(), "storage.xml"));
+
+ synchronized (mLock) {
+ readMetadataLocked();
+ }
+
/*
* Create the connection to vold with a maximum queue of twice the
* amount of containers we'd ever expect to have. This keeps an
@@ -962,6 +1087,61 @@ class MountService extends IMountService.Stub
mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
}
+ private void readMetadataLocked() {
+ mMetadata.clear();
+
+ FileInputStream fis = null;
+ try {
+ fis = mMetadataFile.openRead();
+ final XmlPullParser in = Xml.newPullParser();
+ in.setInput(fis, null);
+
+ int type;
+ while ((type = in.next()) != END_DOCUMENT) {
+ if (type == START_TAG) {
+ final String tag = in.getName();
+ if (TAG_VOLUME.equals(tag)) {
+ final VolumeMetadata meta = VolumeMetadata.read(in);
+ mMetadata.put(meta.fsUuid, meta);
+ }
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // Missing metadata is okay, probably first boot
+ } catch (IOException e) {
+ Slog.wtf(TAG, "Failed reading metadata", e);
+ } catch (XmlPullParserException e) {
+ Slog.wtf(TAG, "Failed reading metadata", e);
+ } finally {
+ IoUtils.closeQuietly(fis);
+ }
+ }
+
+ private void writeMetadataLocked() {
+ FileOutputStream fos = null;
+ try {
+ fos = mMetadataFile.startWrite();
+
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(fos, "utf-8");
+ out.startDocument(null, true);
+ out.startTag(null, TAG_VOLUMES);
+ final int size = mMetadata.size();
+ for (int i = 0; i < size; i++) {
+ final VolumeMetadata meta = mMetadata.valueAt(i);
+ VolumeMetadata.write(out, meta);
+ }
+ out.endTag(null, TAG_VOLUMES);
+ out.endDocument();
+
+ mMetadataFile.finishWrite(fos);
+ } catch (IOException e) {
+ if (fos != null) {
+ mMetadataFile.failWrite(fos);
+ }
+ }
+ }
+
/**
* Exposed API calls below here
*/
@@ -1116,6 +1296,36 @@ class MountService extends IMountService.Stub
}
@Override
+ public void setVolumeNickname(String volId, String nickname) {
+ enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+ waitForReady();
+
+ synchronized (mLock) {
+ final VolumeInfo vol = findVolumeById(volId);
+ final VolumeMetadata meta = findOrCreateMetadataLocked(vol);
+ meta.nickname = nickname;
+ refreshMetadataLocked();
+ writeMetadataLocked();
+ mCallbacks.notifyVolumeMetadataChanged(vol.clone());
+ }
+ }
+
+ @Override
+ public void setVolumeUserFlags(String volId, int flags, int mask) {
+ enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+ waitForReady();
+
+ synchronized (mLock) {
+ final VolumeInfo vol = findVolumeById(volId);
+ final VolumeMetadata meta = findOrCreateMetadataLocked(vol);
+ meta.userFlags = (meta.userFlags & ~mask) | (flags & mask);
+ refreshMetadataLocked();
+ writeMetadataLocked();
+ mCallbacks.notifyVolumeMetadataChanged(vol.clone());
+ }
+ }
+
+ @Override
public int[] getStorageUsers(String path) {
enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
waitForReady();
@@ -1899,7 +2109,12 @@ class MountService extends IMountService.Stub
}
@Override
- public VolumeInfo[] getVolumes() {
+ public VolumeInfo[] getVolumes(int flags) {
+ if ((flags & StorageManager.FLAG_ALL_METADATA) != 0) {
+ // TODO: implement support for returning all metadata
+ throw new UnsupportedOperationException();
+ }
+
synchronized (mLock) {
final VolumeInfo[] res = new VolumeInfo[mVolumes.size()];
for (int i = 0; i < mVolumes.size(); i++) {
@@ -2412,6 +2627,7 @@ class MountService extends IMountService.Stub
private static class Callbacks extends Handler {
private static final int MSG_STORAGE_STATE_CHANGED = 1;
private static final int MSG_VOLUME_STATE_CHANGED = 2;
+ private static final int MSG_VOLUME_METADATA_CHANGED = 3;
private final RemoteCallbackList<IMountServiceListener>
mCallbacks = new RemoteCallbackList<>();
@@ -2455,6 +2671,10 @@ class MountService extends IMountService.Stub
callback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
break;
}
+ case MSG_VOLUME_METADATA_CHANGED: {
+ callback.onVolumeMetadataChanged((VolumeInfo) args.arg1);
+ break;
+ }
}
}
@@ -2473,6 +2693,12 @@ class MountService extends IMountService.Stub
args.argi3 = newState;
obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
}
+
+ private void notifyVolumeMetadataChanged(VolumeInfo vol) {
+ final SomeArgs args = SomeArgs.obtain();
+ args.arg1 = vol;
+ obtainMessage(MSG_VOLUME_METADATA_CHANGED, args).sendToTarget();
+ }
}
@Override
@@ -2529,6 +2755,15 @@ class MountService extends IMountService.Stub
vol.dump(pw);
}
pw.decreaseIndent();
+
+ pw.println();
+ pw.println("Metadata:");
+ pw.increaseIndent();
+ for (int i = 0; i < mMetadata.size(); i++) {
+ final VolumeMetadata meta = mMetadata.valueAt(i);
+ meta.dump(pw);
+ }
+ pw.decreaseIndent();
}
pw.println();
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 794e1b0..772a15c 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -46,7 +46,6 @@ import java.util.ArrayList;
/** This class calls its monitor every minute. Killing this process if they don't return **/
public class Watchdog extends Thread {
static final String TAG = "Watchdog";
- static final boolean localLOGV = false || false;
// Set this to true to use debug default values.
static final boolean DB = false;
@@ -73,7 +72,7 @@ public class Watchdog extends Thread {
static Watchdog sWatchdog;
/* This handler will be used to post message back onto the main thread */
- final ArrayList<HandlerChecker> mHandlerCheckers = new ArrayList<HandlerChecker>();
+ final ArrayList<HandlerChecker> mHandlerCheckers = new ArrayList<>();
final HandlerChecker mMonitorChecker;
ContentResolver mResolver;
ActivityManagerService mActivity;
@@ -191,6 +190,17 @@ public class Watchdog extends Thread {
}
}
+ /** Monitor for checking the availability of binder threads. The monitor will block until
+ * there is a binder thread available to process in coming IPCs to make sure other processes
+ * can still communicate with the service.
+ */
+ private static final class BinderThreadMonitor implements Watchdog.Monitor {
+ @Override
+ public void monitor() {
+ Binder.blockUntilThreadAvailable();
+ }
+ }
+
public interface Monitor {
void monitor();
}
@@ -228,6 +238,9 @@ public class Watchdog extends Thread {
// And the display thread.
mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(),
"display thread", DEFAULT_TIMEOUT));
+
+ // Initialize monitor for Binder threads.
+ addMonitor(new BinderThreadMonitor());
}
public void init(Context context, ActivityManagerService activity) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b606353..f25808b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -26,10 +26,14 @@ import static com.android.internal.util.XmlUtils.readLongAttribute;
import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
-import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
+import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
import static com.android.server.am.ActivityManagerDebugConfig.*;
+import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
-import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -3982,13 +3986,13 @@ public final class ActivityManagerService extends ActivityManagerNative
if (rootR == null) {
Slog.w(TAG, "Finishing task with all activities already finished");
}
- // Do not allow task to finish in Lock Task mode.
- if (tr == mStackSupervisor.mLockTaskModeTask) {
- if (rootR == r) {
- Slog.i(TAG, "Not finishing task in lock task mode");
- mStackSupervisor.showLockTaskToast();
- return false;
- }
+ // Do not allow task to finish if last task in lockTask mode. Launchable apps can
+ // finish themselves.
+ if (tr.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE && rootR == r &&
+ mStackSupervisor.isLastLockedTask(tr)) {
+ Slog.i(TAG, "Not finishing task in lock task mode");
+ mStackSupervisor.showLockTaskToast();
+ return false;
}
if (mController != null) {
// Find the first activity that is not finishing.
@@ -4142,20 +4146,18 @@ public final class ActivityManagerService extends ActivityManagerNative
final long origId = Binder.clearCallingIdentity();
try {
ActivityRecord r = ActivityRecord.isInStackLocked(token);
-
- ActivityRecord rootR = r.task.getRootActivity();
- // Do not allow task to finish in Lock Task mode.
- if (r.task == mStackSupervisor.mLockTaskModeTask) {
- if (rootR == r) {
- mStackSupervisor.showLockTaskToast();
- return false;
- }
+ if (r == null) {
+ return false;
}
- boolean res = false;
- if (r != null) {
- res = r.task.stack.finishActivityAffinityLocked(r);
+
+ // Do not allow the last non-launchable task to finish in Lock Task mode.
+ final TaskRecord task = r.task;
+ if (task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE &&
+ mStackSupervisor.isLastLockedTask(task) && task.getRootActivity() == r) {
+ mStackSupervisor.showLockTaskToast();
+ return false;
}
- return res;
+ return task.stack.finishActivityAffinityLocked(r);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -8336,9 +8338,9 @@ public final class ActivityManagerService extends ActivityManagerNative
final long origId = Binder.clearCallingIdentity();
try {
int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot);
- if (taskId >= 0) {
- if ((mStackSupervisor.mLockTaskModeTask != null)
- && (mStackSupervisor.mLockTaskModeTask.taskId == taskId)) {
+ final TaskRecord task = mRecentTasks.taskForIdLocked(taskId);
+ if (task != null) {
+ if (mStackSupervisor.isLockedTask(task)) {
mStackSupervisor.showLockTaskToast();
return false;
}
@@ -8520,47 +8522,45 @@ public final class ActivityManagerService extends ActivityManagerNative
@Override
public void updateLockTaskPackages(int userId, String[] packages) {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
throw new SecurityException("updateLockTaskPackage called from non-system process");
}
synchronized (this) {
mLockTaskPackages.put(userId, packages);
+ mStackSupervisor.onLockTaskPackagesUpdatedLocked();
}
}
- private boolean isLockTaskAuthorizedLocked(String pkg) {
- String[] packages = mLockTaskPackages.get(mCurrentUserId);
- if (packages == null) {
- return false;
- }
- for (int i = packages.length - 1; i >= 0; --i) {
- if (pkg.equals(packages[i])) {
- return true;
- }
- }
- return false;
- }
void startLockTaskModeLocked(TaskRecord task) {
- final String pkg = task.intent.getComponent().getPackageName();
+ if (task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
+ return;
+ }
+
// isSystemInitiated is used to distinguish between locked and pinned mode, as pinned mode
// is initiated by system after the pinning request was shown and locked mode is initiated
// by an authorized app directly
- boolean isSystemInitiated = Binder.getCallingUid() == Process.SYSTEM_UID;
+ final int callingUid = Binder.getCallingUid();
+ boolean isSystemInitiated = callingUid == Process.SYSTEM_UID;
long ident = Binder.clearCallingIdentity();
try {
- if (!isSystemInitiated && !isLockTaskAuthorizedLocked(pkg)) {
- StatusBarManagerInternal statusBarManager =
- LocalServices.getService(StatusBarManagerInternal.class);
- if (statusBarManager != null) {
- statusBarManager.showScreenPinningRequest();
+ final ActivityStack stack = mStackSupervisor.getFocusedStack();
+ if (!isSystemInitiated) {
+ task.mLockTaskUid = callingUid;
+ if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) {
+ // startLockTask() called by app and task mode is lockTaskModeDefault.
+ StatusBarManagerInternal statusBarManager =
+ LocalServices.getService(StatusBarManagerInternal.class);
+ if (statusBarManager != null) {
+ statusBarManager.showScreenPinningRequest();
+ }
+ return;
}
- return;
- }
- final ActivityStack stack = mStackSupervisor.getFocusedStack();
- if (!isSystemInitiated && (stack == null || task != stack.topTask())) {
- throw new IllegalArgumentException("Invalid task, not in foreground");
+ if (stack == null || task != stack.topTask()) {
+ throw new IllegalArgumentException("Invalid task, not in foreground");
+ }
}
mStackSupervisor.setLockTaskModeLocked(task, isSystemInitiated ?
ActivityManager.LOCK_TASK_MODE_PINNED :
@@ -8614,23 +8614,15 @@ public final class ActivityManagerService extends ActivityManagerNative
@Override
public void stopLockTaskMode() {
- // Verify that the user matches the package of the intent for the TaskRecord
- // we are locked to or systtem. This will ensure the same caller for startLockTaskMode
- // and stopLockTaskMode.
- final int callingUid = Binder.getCallingUid();
- if (callingUid != Process.SYSTEM_UID) {
- try {
- String pkg =
- mStackSupervisor.mLockTaskModeTask.intent.getComponent().getPackageName();
- int uid = mContext.getPackageManager().getPackageUid(pkg,
- Binder.getCallingUserHandle().getIdentifier());
- if (uid != callingUid) {
- throw new SecurityException("Invalid uid, expected " + uid);
- }
- } catch (NameNotFoundException e) {
- Log.d(TAG, "stopLockTaskMode " + e);
- return;
- }
+ final TaskRecord lockTask = mStackSupervisor.getLockedTaskLocked();
+ if (lockTask == null) {
+ // Our work here is done.
+ return;
+ }
+ // Ensure the same caller for startLockTaskMode and stopLockTaskMode.
+ if (getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_LOCKED &&
+ Binder.getCallingUid() != lockTask.mLockTaskUid) {
+ throw new SecurityException("Invalid uid, expected " + lockTask.mLockTaskUid);
}
long ident = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 2362d28..6210d60 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -2875,7 +2875,7 @@ final class ActivityStack {
}
if (endTask) {
- mStackSupervisor.endLockTaskModeIfTaskEnding(task);
+ mStackSupervisor.removeLockedTaskLocked(task);
}
} else if (r.state != ActivityState.PAUSING) {
// If the activity is PAUSING, we will complete the finish once
@@ -3674,8 +3674,7 @@ final class ActivityStack {
}
Slog.i(TAG, "moveTaskToBack: " + tr);
-
- mStackSupervisor.endLockTaskModeIfTaskEnding(tr);
+ mStackSupervisor.removeLockedTaskLocked(tr);
// If we have a watcher, preflight the move before committing to it. First check
// for *other* available tasks, but if none are available, then try again allowing the
@@ -4240,7 +4239,7 @@ final class ActivityStack {
*/
void removeTask(TaskRecord task, String reason, boolean notMoving) {
if (notMoving) {
- mStackSupervisor.endLockTaskModeIfTaskEnding(task);
+ mStackSupervisor.removeLockedTaskLocked(task);
mWindowManager.removeTask(task.taskId);
}
@@ -4345,4 +4344,10 @@ final class ActivityStack {
mFullscreen = Configuration.EMPTY.equals(mOverrideConfig);
return !mOverrideConfig.equals(oldConfig);
}
+
+ void onLockTaskPackagesUpdatedLocked() {
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ mTaskHistory.get(taskNdx).setLockTaskAuth();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index c2f6bfd..6908483 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -17,10 +17,14 @@
package com.android.server.am;
import static android.Manifest.permission.START_ANY_ACTIVITY;
+import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.server.am.ActivityManagerDebugConfig.*;
import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
@@ -28,6 +32,10 @@ import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
import static com.android.server.am.ActivityStack.ActivityState.*;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
import android.app.Activity;
import android.app.ActivityManager;
@@ -261,17 +269,17 @@ public final class ActivityStackSupervisor implements DisplayListener {
// TODO: Add listener for removal of references.
/** Mapping from (ActivityStack/TaskStack).mStackId to their current state */
- private SparseArray<ActivityContainer> mActivityContainers = new SparseArray<ActivityContainer>();
+ private SparseArray<ActivityContainer> mActivityContainers = new SparseArray<>();
/** Mapping from displayId to display current state */
- private final SparseArray<ActivityDisplay> mActivityDisplays =
- new SparseArray<ActivityDisplay>();
+ private final SparseArray<ActivityDisplay> mActivityDisplays = new SparseArray<>();
InputManagerInternal mInputManagerInternal;
- /** If non-null then the task specified remains in front and no other tasks may be started
- * until the task exits or #stopLockTaskMode() is called. */
- TaskRecord mLockTaskModeTask;
+ /** The chain of tasks in lockTask mode. The current frontmost task is at the top, and tasks
+ * may be finished until there is only one entry left. If this is empty the system is not
+ * in lockTask mode. */
+ ArrayList<TaskRecord> mLockTaskModeTasks = new ArrayList<>();
/** Store the current lock task mode. Possible values:
* {@link ActivityManager#LOCK_TASK_MODE_NONE}, {@link ActivityManager#LOCK_TASK_MODE_LOCKED},
* {@link ActivityManager#LOCK_TASK_MODE_PINNED}
@@ -282,8 +290,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
*/
private LockTaskNotify mLockTaskNotify;
- final ArrayList<PendingActivityLaunch> mPendingActivityLaunches
- = new ArrayList<PendingActivityLaunch>();
+ final ArrayList<PendingActivityLaunch> mPendingActivityLaunches = new ArrayList<>();
/** Used to keep resumeTopActivityLocked() from being entered recursively */
boolean inResumeTopActivity;
@@ -796,7 +803,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
- ArrayList<RunningTaskInfo> stackTaskList = new ArrayList<RunningTaskInfo>();
+ ArrayList<RunningTaskInfo> stackTaskList = new ArrayList<>();
runningTaskLists.add(stackTaskList);
stack.getTasksLocked(stackTaskList, callingUid, allowed);
}
@@ -894,8 +901,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
intent = new Intent(intent);
// Collect information about the target of the Intent.
- ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags,
- profilerInfo, userId);
+ ActivityInfo aInfo =
+ resolveActivity(intent, resolvedType, startFlags, profilerInfo, userId);
ActivityContainer container = (ActivityContainer)iContainer;
synchronized (mService) {
@@ -1170,7 +1177,12 @@ public final class ActivityStackSupervisor implements DisplayListener {
mService.updateLruProcessLocked(app, true, null);
mService.updateOomAdjLocked();
- final ActivityStack stack = r.task.stack;
+ final TaskRecord task = r.task;
+ if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) {
+ setLockTaskModeLocked(task, LOCK_TASK_MODE_LOCKED, "lockTaskLaunchMode attribute");
+ }
+
+ final ActivityStack stack = task.stack;
try {
if (app.thread == null) {
throw new RemoteException();
@@ -1187,11 +1199,11 @@ public final class ActivityStackSupervisor implements DisplayListener {
if (andResume) {
EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY,
r.userId, System.identityHashCode(r),
- r.task.taskId, r.shortComponentName);
+ task.taskId, r.shortComponentName);
}
if (r.isHomeActivity() && r.isNotResolverActivity()) {
// Home process is the root process of the task.
- mService.mHomeProcess = r.task.mActivities.get(0).app;
+ mService.mHomeProcess = task.mActivities.get(0).app;
}
mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
r.sleeping = false;
@@ -1233,7 +1245,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
new Configuration(stack.mOverrideConfig), r.compat, r.launchedFromPackage,
- r.task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
+ task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);
if ((app.info.privateFlags&ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
@@ -1946,7 +1958,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
intentActivity = targetStack.resetTaskIfNeededLocked(intentActivity, r);
}
- if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
+ if ((startFlags & ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
// We don't need to start a new activity, and
// the client said not to do anything if that
// is the case, so this is it! And for paranoia, make
@@ -1964,8 +1976,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
return ActivityManager.START_RETURN_INTENT_TO_CALLER;
}
- if ((launchFlags &
- (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
+ if ((launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
// The caller has requested to completely replace any
// existing task with its new activity. Well that should
@@ -2128,10 +2139,6 @@ public final class ActivityStackSupervisor implements DisplayListener {
// Should this be considered a new task?
if (r.resultTo == null && inTask == null && !addingToTask
&& (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
- if (isLockTaskModeViolation(reuseTask)) {
- Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
- return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
- }
newTask = true;
targetStack = computeStackFocus(r, newTask);
targetStack.moveToFront("startingNewTask");
@@ -2147,6 +2154,10 @@ public final class ActivityStackSupervisor implements DisplayListener {
} else {
r.setTask(reuseTask, taskToAffiliate);
}
+ if (isLockTaskModeViolation(r.task)) {
+ Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
+ return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
+ }
if (!movedHome) {
if ((launchFlags &
(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
@@ -3292,6 +3303,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
pw.print(prefix); pw.println("mCurTaskId=" + mCurTaskId);
pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
pw.print(prefix); pw.println("mActivityContainers=" + mActivityContainers);
+ pw.print(prefix); pw.println("mLockTaskModeTasks" + mLockTaskModeTasks);
}
ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
@@ -3592,6 +3604,32 @@ public final class ActivityStackSupervisor implements DisplayListener {
return list;
}
+ TaskRecord getLockedTaskLocked() {
+ final int top = mLockTaskModeTasks.size() - 1;
+ if (top >= 0) {
+ return mLockTaskModeTasks.get(top);
+ }
+ return null;
+ }
+
+ boolean isLockedTask(TaskRecord task) {
+ return mLockTaskModeTasks.contains(task);
+ }
+
+ boolean isLastLockedTask(TaskRecord task) {
+ return mLockTaskModeTasks.size() == 1 && mLockTaskModeTasks.contains(task);
+ }
+
+ void removeLockedTaskLocked(final TaskRecord task) {
+ if (mLockTaskModeTasks.remove(task) && mLockTaskModeTasks.isEmpty()) {
+ // Last one.
+ final Message lockTaskMsg = Message.obtain();
+ lockTaskMsg.arg1 = task.userId;
+ lockTaskMsg.what = LOCK_TASK_END_MSG;
+ mHandler.sendMessage(lockTaskMsg);
+ }
+ }
+
void showLockTaskToast() {
mLockTaskNotify.showToast(mLockTaskModeState);
}
@@ -3599,42 +3637,93 @@ public final class ActivityStackSupervisor implements DisplayListener {
void setLockTaskModeLocked(TaskRecord task, int lockTaskModeState, String reason) {
if (task == null) {
// Take out of lock task mode if necessary
- if (mLockTaskModeTask != null) {
- final Message lockTaskMsg = Message.obtain();
- lockTaskMsg.arg1 = mLockTaskModeTask.userId;
- lockTaskMsg.what = LOCK_TASK_END_MSG;
- mLockTaskModeTask = null;
- mHandler.sendMessage(lockTaskMsg);
+ final TaskRecord lockedTask = getLockedTaskLocked();
+ if (lockedTask != null) {
+ removeLockedTaskLocked(lockedTask);
+ if (!mLockTaskModeTasks.isEmpty()) {
+ // There are locked tasks remaining, can only finish this task, not unlock it.
+ lockedTask.performClearTaskLocked();
+ resumeTopActivitiesLocked();
+ return;
+ }
}
return;
}
+
+ // Should have already been checked, but do it again.
+ if (task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
+ return;
+ }
if (isLockTaskModeViolation(task)) {
- Slog.e(TAG, "setLockTaskMode: Attempt to start a second Lock Task Mode task.");
+ Slog.e(TAG, "setLockTaskMode: Attempt to start an unauthorized lock task.");
return;
}
- mLockTaskModeTask = task;
+
+ if (mLockTaskModeTasks.isEmpty()) {
+ // First locktask.
+ final Message lockTaskMsg = Message.obtain();
+ lockTaskMsg.obj = task.intent.getComponent().getPackageName();
+ lockTaskMsg.arg1 = task.userId;
+ lockTaskMsg.what = LOCK_TASK_START_MSG;
+ lockTaskMsg.arg2 = lockTaskModeState;
+ mHandler.sendMessage(lockTaskMsg);
+ }
+ // Add it or move it to the top.
+ mLockTaskModeTasks.remove(task);
+ mLockTaskModeTasks.add(task);
+
+ if (task.mLockTaskUid == -1) {
+ task.mLockTaskUid = task.mCallingUid;
+ }
findTaskToMoveToFrontLocked(task, 0, null, reason);
resumeTopActivitiesLocked();
-
- final Message lockTaskMsg = Message.obtain();
- lockTaskMsg.obj = mLockTaskModeTask.intent.getComponent().getPackageName();
- lockTaskMsg.arg1 = mLockTaskModeTask.userId;
- lockTaskMsg.what = LOCK_TASK_START_MSG;
- lockTaskMsg.arg2 = lockTaskModeState;
- mHandler.sendMessage(lockTaskMsg);
}
boolean isLockTaskModeViolation(TaskRecord task) {
- return mLockTaskModeTask != null && mLockTaskModeTask != task;
+ if (getLockedTaskLocked() == task) {
+ return false;
+ }
+ final int lockTaskAuth = task.mLockTaskAuth;
+ switch (lockTaskAuth) {
+ case LOCK_TASK_AUTH_DONT_LOCK:
+ return !mLockTaskModeTasks.isEmpty();
+ case LOCK_TASK_AUTH_LAUNCHABLE:
+ case LOCK_TASK_AUTH_WHITELISTED:
+ return false;
+ case LOCK_TASK_AUTH_PINNABLE:
+ // Pinnable tasks can't be launched on top of locktask tasks.
+ return !mLockTaskModeTasks.isEmpty();
+ default:
+ Slog.w(TAG, "isLockTaskModeViolation: invalid lockTaskAuth value=" + lockTaskAuth);
+ return true;
+ }
}
- void endLockTaskModeIfTaskEnding(TaskRecord task) {
- if (mLockTaskModeTask != null && mLockTaskModeTask == task) {
- final Message lockTaskMsg = Message.obtain();
- lockTaskMsg.arg1 = mLockTaskModeTask.userId;
- lockTaskMsg.what = LOCK_TASK_END_MSG;
- mLockTaskModeTask = null;
- mHandler.sendMessage(lockTaskMsg);
+ void onLockTaskPackagesUpdatedLocked() {
+ boolean didSomething = false;
+ for (int taskNdx = mLockTaskModeTasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ final TaskRecord lockedTask = mLockTaskModeTasks.get(taskNdx);
+ if (lockedTask.mLockTaskMode != LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED) {
+ continue;
+ }
+ final boolean wasLaunchable = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE;
+ lockedTask.setLockTaskAuth();
+ if (wasLaunchable && lockedTask.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE) {
+ // Lost whitelisting authorization. End it now.
+ removeLockedTaskLocked(lockedTask);
+ lockedTask.performClearTaskLocked();
+ didSomething = true;
+ }
+ }
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = stacks.get(stackNdx);
+ stack.onLockTaskPackagesUpdatedLocked();
+ }
+ }
+ if (didSomething) {
+ resumeTopActivitiesLocked();
}
}
@@ -3734,11 +3823,10 @@ public final class ActivityStackSupervisor implements DisplayListener {
mLockTaskModeState = msg.arg2;
if (getStatusBarService() != null) {
int flags = 0;
- if (mLockTaskModeState == ActivityManager.LOCK_TASK_MODE_LOCKED) {
+ if (mLockTaskModeState == LOCK_TASK_MODE_LOCKED) {
flags = StatusBarManager.DISABLE_MASK
& (~StatusBarManager.DISABLE_BACK);
- } else if (mLockTaskModeState ==
- ActivityManager.LOCK_TASK_MODE_PINNED) {
+ } else if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
flags = StatusBarManager.DISABLE_MASK
& (~StatusBarManager.DISABLE_BACK)
& (~StatusBarManager.DISABLE_HOME)
@@ -3776,8 +3864,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
boolean shouldLockKeyguard = Settings.Secure.getInt(
mService.mContext.getContentResolver(),
Settings.Secure.LOCK_TO_APP_EXIT_LOCKED) != 0;
- if (mLockTaskModeState == ActivityManager.LOCK_TASK_MODE_PINNED &&
- shouldLockKeyguard) {
+ if (mLockTaskModeState == LOCK_TASK_MODE_PINNED && shouldLockKeyguard) {
mWindowManager.lockNow(null);
mWindowManager.dismissKeyguard();
new LockPatternUtils(mService.mContext)
@@ -3789,7 +3876,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
} catch (RemoteException ex) {
throw new RuntimeException(ex);
} finally {
- mLockTaskModeState = ActivityManager.LOCK_TASK_MODE_NONE;
+ mLockTaskModeState = LOCK_TASK_MODE_NONE;
}
} break;
case CONTAINER_CALLBACK_TASK_LIST_EMPTY: {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 82e6d47..790a78d 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -18,6 +18,10 @@ package com.android.server.am;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
import static com.android.server.am.ActivityManagerDebugConfig.*;
import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
@@ -121,6 +125,20 @@ final class TaskRecord {
boolean mResizeable; // Activities in the task resizeable. Based on the resizable setting of
// the root activity.
+ int mLockTaskMode; // Which tasklock mode to launch this task in. One of
+ // ActivityManager.LOCK_TASK_LAUNCH_MODE_*
+ /** Can't be put in lockTask mode. */
+ final static int LOCK_TASK_AUTH_DONT_LOCK = 0;
+ /** Can enter lockTask with user approval if not already in lockTask. */
+ final static int LOCK_TASK_AUTH_PINNABLE = 1;
+ /** Starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing lockTask task. */
+ final static int LOCK_TASK_AUTH_LAUNCHABLE = 2;
+ /** Enters LOCK_TASK_MODE_LOCKED via startLockTask(), enters LOCK_TASK_MODE_PINNED from
+ * Overview. Can start over existing lockTask task. */
+ final static int LOCK_TASK_AUTH_WHITELISTED = 3;
+ int mLockTaskAuth = LOCK_TASK_AUTH_PINNABLE;
+
+ int mLockTaskUid = -1; // The uid of the application that called startLockTask().
// This represents the last resolved activity values for this task
// NOTE: This value needs to be persisted with each task
@@ -186,6 +204,8 @@ final class TaskRecord {
voiceInteractor = _voiceInteractor;
isAvailable = true;
mActivities = new ArrayList<>();
+ mCallingUid = info.applicationInfo.uid;
+ mCallingPackage = info.packageName;
setIntent(_intent, info);
}
@@ -201,12 +221,12 @@ final class TaskRecord {
voiceInteractor = null;
isAvailable = true;
mActivities = new ArrayList<>();
+ mCallingUid = info.applicationInfo.uid;
+ mCallingPackage = info.packageName;
setIntent(_intent, info);
taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
isPersistable = true;
- mCallingUid = info.applicationInfo.uid;
- mCallingPackage = info.packageName;
// Clamp to [1, max].
maxRecents = Math.min(Math.max(info.maxRecents, 1),
ActivityManager.getMaxAppRecentsLimitStatic());
@@ -215,8 +235,6 @@ final class TaskRecord {
mTaskToReturnTo = HOME_ACTIVITY_TYPE;
userId = UserHandle.getUserId(info.applicationInfo.uid);
lastTaskDescription = _taskDescription;
- mCallingUid = info.applicationInfo.uid;
- mCallingPackage = info.packageName;
}
private TaskRecord(ActivityManagerService service, int _taskId, Intent _intent,
@@ -278,9 +296,9 @@ final class TaskRecord {
/** Sets the original intent, and the calling uid and package. */
void setIntent(ActivityRecord r) {
- setIntent(r.intent, r.info);
mCallingUid = r.launchedFromUid;
mCallingPackage = r.launchedFromPackage;
+ setIntent(r.intent, r.info);
}
/** Sets the original intent, _without_ updating the calling uid or package. */
@@ -362,6 +380,8 @@ final class TaskRecord {
autoRemoveRecents = false;
}
mResizeable = info.resizeable;
+ mLockTaskMode = info.lockTaskLaunchMode;
+ setLockTaskAuth();
}
void setTaskToReturnTo(int taskToReturnTo) {
@@ -716,6 +736,53 @@ final class TaskRecord {
performClearTaskAtIndexLocked(0);
}
+ private boolean isPrivileged() {
+ final ProcessRecord proc = mService.mProcessNames.get(mCallingPackage, mCallingUid);
+ if (proc != null) {
+ return (proc.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
+ }
+ return false;
+ }
+
+ void setLockTaskAuth() {
+ switch (mLockTaskMode) {
+ case LOCK_TASK_LAUNCH_MODE_DEFAULT:
+ mLockTaskAuth = isLockTaskWhitelistedLocked() ?
+ LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE;
+ break;
+
+ case LOCK_TASK_LAUNCH_MODE_NEVER:
+ mLockTaskAuth = isPrivileged() ?
+ LOCK_TASK_AUTH_DONT_LOCK : LOCK_TASK_AUTH_PINNABLE;
+ break;
+
+ case LOCK_TASK_LAUNCH_MODE_ALWAYS:
+ mLockTaskAuth = isPrivileged() ?
+ LOCK_TASK_AUTH_LAUNCHABLE: LOCK_TASK_AUTH_PINNABLE;
+ break;
+
+ case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED:
+ mLockTaskAuth = isLockTaskWhitelistedLocked() ?
+ LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE;
+ break;
+ }
+ }
+
+ boolean isLockTaskWhitelistedLocked() {
+ if (mCallingPackage == null) {
+ return false;
+ }
+ String[] packages = mService.mLockTaskPackages.get(userId);
+ if (packages == null) {
+ return false;
+ }
+ for (int i = packages.length - 1; i >= 0; --i) {
+ if (mCallingPackage.equals(packages[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
boolean isHomeTask() {
return taskType == HOME_ACTIVITY_TYPE;
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 2d1f939..8a7c902 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -71,7 +71,10 @@ public class NetworkAgentInfo {
private static final int UNVALIDATED_SCORE_PENALTY = 40;
// Score for explicitly connected network.
- private static final int EXPLICITLY_SELECTED_NETWORK_SCORE = 100;
+ //
+ // This ensures that a) the explicitly selected network is never trumped by anything else, and
+ // b) the explicitly selected network is never torn down.
+ private static final int MAXIMUM_NETWORK_SCORE = 100;
// The list of NetworkRequests being satisfied by this Network.
public final SparseArray<NetworkRequest> networkRequests = new SparseArray<NetworkRequest>();
@@ -123,13 +126,18 @@ public class NetworkAgentInfo {
// score. The NetworkScore class would provide a nice place to centralize score constants
// so they are not scattered about the transports.
- int score = currentScore;
+ // If this network is explicitly selected and the user has decided to use it even if it's
+ // unvalidated, give it the maximum score. Also give it the maximum score if it's explicitly
+ // selected and we're trying to see what its score could be. This ensures that we don't tear
+ // down an explicitly selected network before the user gets a chance to prefer it when
+ // a higher-scoring network (e.g., Ethernet) is available.
+ if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) {
+ return MAXIMUM_NETWORK_SCORE;
+ }
+ int score = currentScore;
if (!everValidated && !pretendValidated) score -= UNVALIDATED_SCORE_PENALTY;
if (score < 0) score = 0;
-
- if (networkMisc.explicitlySelected) score = EXPLICITLY_SELECTED_NETWORK_SCORE;
-
return score;
}
@@ -156,7 +164,9 @@ public class NetworkAgentInfo {
networkCapabilities + "} Score{" + getCurrentScore() + "} " +
"everValidated{" + everValidated + "} lastValidated{" + lastValidated + "} " +
"created{" + created + "} " +
- "explicitlySelected{" + networkMisc.explicitlySelected + "} }";
+ "explicitlySelected{" + networkMisc.explicitlySelected + "} " +
+ "acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} " +
+ "}";
}
public String name() {
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 4173b78..7cccef2 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -1190,7 +1190,7 @@ public class SyncManager {
while (operationIterator.hasNext()) {
final SyncOperation op = operationIterator.next();
if (op.appIdle
- && getPackageName(op.target).equals(packageName)
+ && (packageName == null || getPackageName(op.target).equals(packageName))
&& (userId == UserHandle.USER_ALL || op.target.userId == userId)) {
op.appIdle = false;
clearBackoffSetting(op);
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index c150b60..0ca0c0e 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -39,6 +39,7 @@ import static android.Manifest.permission.USE_FINGERPRINT;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -51,9 +52,9 @@ import java.util.List;
public class FingerprintService extends SystemService {
private static final String TAG = "FingerprintService";
private static final boolean DEBUG = true;
- private ClientData mAuthClient = null;
- private ClientData mEnrollClient = null;
- private ClientData mRemoveClient = null;
+ private ClientMonitor mAuthClient = null;
+ private ClientMonitor mEnrollClient = null;
+ private ClientMonitor mRemoveClient = null;
private static final int MSG_NOTIFY = 10;
@@ -63,7 +64,6 @@ public class FingerprintService extends SystemService {
// Must agree with the list in fingerprint.h
private static final int FINGERPRINT_ERROR = -1;
private static final int FINGERPRINT_ACQUIRED = 1;
- private static final int FINGERPRINT_PROCESSED = 2;
private static final int FINGERPRINT_TEMPLATE_ENROLLING = 3;
private static final int FINGERPRINT_TEMPLATE_REMOVED = 4;
private static final int FINGERPRINT_AUTHENTICATED = 5;
@@ -83,80 +83,21 @@ public class FingerprintService extends SystemService {
};
private Context mContext;
private int mHalDeviceId;
+ private int mFailedAttempts;
+ private final Runnable mLockoutReset = new Runnable() {
+ @Override
+ public void run() {
+ resetFailedAttempts();
+ }
+ };
private static final int STATE_IDLE = 0;
private static final int STATE_AUTHENTICATING = 1;
private static final int STATE_ENROLLING = 2;
private static final int STATE_REMOVING = 3;
private static final long MS_PER_SEC = 1000;
-
- private class ClientData {
- IBinder token;
- IFingerprintServiceReceiver receiver;
- int userId;
- long opId;
- private TokenWatcher tokenWatcher;
- public ClientData(IBinder token, long opId, IFingerprintServiceReceiver receiver,
- int userId) {
- this.token = token;
- this.opId = opId;
- this.receiver = receiver;
- this.userId = userId;
- tokenWatcher = new TokenWatcher(token);
- try {
- token.linkToDeath(tokenWatcher, 0);
- } catch (RemoteException e) {
- Slog.w(TAG, "caught remote exception in linkToDeath: ", e);
- }
- }
-
- IBinder getToken() {
- return tokenWatcher.getToken();
- }
-
- public void destroy() {
- token.unlinkToDeath(tokenWatcher, 0);
- tokenWatcher.token = null;
- }
- }
-
- private class TokenWatcher implements IBinder.DeathRecipient {
- WeakReference<IBinder> token;
-
- TokenWatcher(IBinder token) {
- this.token = new WeakReference<IBinder>(token);
- }
-
- IBinder getToken() {
- return token.get();
- }
-
- public void binderDied() {
- if (mAuthClient != null & mAuthClient.token == token)
- mAuthClient = null;
- if (mEnrollClient != null && mEnrollClient.token == token)
- mEnrollClient = null;
- this.token = null;
- }
-
- protected void finalize() throws Throwable {
- try {
- if (token != null) {
- if (DEBUG) Slog.w(TAG, "removing leaked reference: " + token);
- if (mAuthClient != null && mAuthClient.token == token) {
- mAuthClient.destroy();
- mAuthClient = null;
- }
- if (mEnrollClient != null && mEnrollClient.token == token) {
- mAuthClient.destroy();
- mEnrollClient = null;
- }
- }
- } finally {
- super.finalize();
- }
- }
- }
+ private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30*1000;
+ private static final int MAX_FAILED_ATTEMPTS = 5;
public FingerprintService(Context context) {
super(context);
@@ -166,11 +107,11 @@ public class FingerprintService extends SystemService {
// TODO: Move these into separate process
// JNI methods to communicate from FingerprintService to HAL
- static native int nativeEnroll(long challenge, int groupId, int timeout);
+ static native int nativeEnroll(byte [] token, int groupId, int timeout);
static native long nativePreEnroll();
static native int nativeStopEnrollment();
static native int nativeAuthenticate(long sessionId, int groupId);
- static native int nativeStopAuthentication(long sessionId);
+ static native int nativeStopAuthentication();
static native int nativeRemove(int fingerId, int groupId);
static native int nativeOpenHal();
static native int nativeCloseHal();
@@ -201,82 +142,62 @@ public class FingerprintService extends SystemService {
Slog.v(TAG, "handleNotify(type=" + type + ", arg1=" + arg1 + ", arg2=" + arg2 + ")"
+ ", mAuthClients = " + mAuthClient + ", mEnrollClient = " + mEnrollClient);
if (mEnrollClient != null) {
- try {
- final IBinder token = mEnrollClient.token;
- if (doNotify(mEnrollClient, type, arg1, arg2, arg3)) {
- stopEnrollment(token);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "can't send message to mEnrollClient. Did it die?", e);
- mEnrollClient.destroy();
- mEnrollClient = null;
+ final IBinder token = mEnrollClient.token;
+ if (doNotify(mEnrollClient, type, arg1, arg2, arg3)) {
+ stopEnrollment(token);
}
}
if (mAuthClient != null) {
- try {
- final IBinder token = mAuthClient.getToken();
- if (doNotify(mAuthClient, type, arg1, arg2, arg3)) {
- stopAuthentication(token);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "can't send message to mAuthClient. Did it die?", e);
- mAuthClient.destroy();
- mAuthClient = null;
+ final IBinder token = mAuthClient.token;
+ if (doNotify(mAuthClient, type, arg1, arg2, arg3)) {
+ stopAuthentication(token);
}
}
if (mRemoveClient != null) {
- try {
- if (doNotify(mRemoveClient, type, arg1, arg2, arg3)) {
- mRemoveClient.destroy();
- mRemoveClient = null;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "can't send message to mRemoveClient. Did it die?", e);
- mRemoveClient.destroy();
- mRemoveClient = null;
+ if (doNotify(mRemoveClient, type, arg1, arg2, arg3)) {
+ removeClient(mRemoveClient);
}
}
}
// Returns true if the operation is done, i.e. authentication completed
- boolean doNotify(ClientData clientData, int type, int arg1, int arg2, int arg3)
- throws RemoteException {
- if (clientData.receiver == null) {
- if (DEBUG) Slog.v(TAG, "receiver not registered!!");
- return false;
- }
+ boolean doNotify(ClientMonitor clientMonitor, int type, int arg1, int arg2, int arg3) {
ContentResolver contentResolver = mContext.getContentResolver();
boolean operationCompleted = false;
switch (type) {
case FINGERPRINT_ERROR:
- clientData.receiver.onError(mHalDeviceId, arg1 /* error */);
- if (arg1 == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
- if (mEnrollClient != null) {
- mEnrollClient.destroy();
- mEnrollClient = null;
- }
- if (mAuthClient != null) {
- mAuthClient.destroy();
- mAuthClient = null;
- }
+ {
+ final int error = arg1;
+ clientMonitor.sendError(error);
+ removeClient(clientMonitor);
+ operationCompleted = true; // any error means the operation is done
}
- operationCompleted = true; // any error means the operation is done
break;
case FINGERPRINT_ACQUIRED:
- clientData.receiver.onAcquired(mHalDeviceId, arg1 /* acquireInfo */);
+ clientMonitor.sendAcquired(arg1 /* acquireInfo */);
break;
- case FINGERPRINT_PROCESSED:
- clientData.receiver.onProcessed(mHalDeviceId, arg1 /* fpId */, arg2 /* gpId */);
- operationCompleted = true; // we either got a positive or negative match
+ case FINGERPRINT_AUTHENTICATED:
+ {
+ final int fpId = arg1;
+ final int groupId = arg2;
+ clientMonitor.sendAuthenticated(fpId, groupId);
+ if (fpId == 0) {
+ if (clientMonitor == mAuthClient) {
+ operationCompleted = handleFailedAttempt(clientMonitor);
+ }
+ } else {
+ mLockoutReset.run(); // a valid fingerprint resets lockout
+ }
+ }
break;
case FINGERPRINT_TEMPLATE_ENROLLING:
{
final int fpId = arg1;
final int groupId = arg2;
final int remaining = arg3;
- clientData.receiver.onEnrollResult(mHalDeviceId, fpId, groupId, remaining);
+ clientMonitor.sendEnrollResult(fpId, groupId, remaining);
if (remaining == 0) {
- addTemplateForUser(clientData, contentResolver, fpId);
+ addTemplateForUser(clientMonitor, contentResolver, fpId);
operationCompleted = true; // enroll completed
}
}
@@ -285,11 +206,11 @@ public class FingerprintService extends SystemService {
{
final int fingerId = arg1;
final int groupId = arg2;
- removeTemplateForUser(clientData, contentResolver, fingerId);
+ removeTemplateForUser(clientMonitor, contentResolver, fingerId);
if (fingerId == 0) {
operationCompleted = true; // remove completed
} else {
- clientData.receiver.onRemoved(mHalDeviceId, fingerId, groupId);
+ clientMonitor.sendRemoved(fingerId, groupId);
}
}
break;
@@ -297,24 +218,60 @@ public class FingerprintService extends SystemService {
return operationCompleted;
}
- private void removeTemplateForUser(ClientData clientData, ContentResolver contentResolver,
+ private void removeClient(ClientMonitor clientMonitor) {
+ if (clientMonitor == null) return;
+ clientMonitor.destroy();
+ if (clientMonitor == mAuthClient) {
+ mAuthClient = null;
+ } else if (clientMonitor == mEnrollClient) {
+ mEnrollClient = null;
+ } else if (clientMonitor == mRemoveClient) {
+ mRemoveClient = null;
+ }
+ }
+
+ private boolean inLockoutMode() {
+ return mFailedAttempts > MAX_FAILED_ATTEMPTS;
+ }
+
+ private void resetFailedAttempts() {
+ if (DEBUG) Slog.v(TAG, "Reset fingerprint lockout");
+ mFailedAttempts = 0;
+ }
+
+ private boolean handleFailedAttempt(ClientMonitor clientMonitor) {
+ mFailedAttempts++;
+ if (mFailedAttempts > MAX_FAILED_ATTEMPTS) {
+ // Failing multiple times will continue to push out the lockout time.
+ mHandler.removeCallbacks(mLockoutReset);
+ mHandler.postDelayed(mLockoutReset, FAIL_LOCKOUT_TIMEOUT_MS);
+ if (clientMonitor != null
+ && !clientMonitor.sendError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)) {
+ Slog.w(TAG, "Cannot send lockout message to client");
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void removeTemplateForUser(ClientMonitor clientMonitor, ContentResolver contentResolver,
final int fingerId) {
FingerprintUtils.removeFingerprintIdForUser(fingerId, contentResolver,
- clientData.userId);
+ clientMonitor.userId);
}
- private void addTemplateForUser(ClientData clientData, ContentResolver contentResolver,
+ private void addTemplateForUser(ClientMonitor clientMonitor, ContentResolver contentResolver,
final int fingerId) {
FingerprintUtils.addFingerprintIdForUser(contentResolver, fingerId,
- clientData.userId);
+ clientMonitor.userId);
}
- void startEnrollment(IBinder token, long opId,
- int groupId, IFingerprintServiceReceiver receiver, int flags) {
+ void startEnrollment(IBinder token, byte[] cryptoToken, int groupId,
+ IFingerprintServiceReceiver receiver, int flags) {
stopPendingOperations();
- mEnrollClient = new ClientData(token, opId, receiver, groupId);
+ mEnrollClient = new ClientMonitor(token, receiver, groupId);
final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
- final int result = nativeEnroll(opId, groupId, timeout);
+ final int result = nativeEnroll(cryptoToken, groupId, timeout);
if (result != 0) {
Slog.w(TAG, "startEnroll failed, result=" + result);
}
@@ -335,8 +292,11 @@ public class FingerprintService extends SystemService {
}
void stopEnrollment(IBinder token) {
- if (mEnrollClient == null || mEnrollClient.token != token) return;
+ final ClientMonitor client = mEnrollClient;
+ if (client == null || client.token != token) return;
int result = nativeStopEnrollment();
+ client.sendError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
+ removeClient(mEnrollClient);
if (result != 0) {
Slog.w(TAG, "startEnrollCancel failed, result=" + result);
}
@@ -345,7 +305,15 @@ public class FingerprintService extends SystemService {
void startAuthentication(IBinder token, long opId, int groupId,
IFingerprintServiceReceiver receiver, int flags) {
stopPendingOperations();
- mAuthClient = new ClientData(token, opId, receiver, groupId);
+ mAuthClient = new ClientMonitor(token, receiver, groupId);
+ if (inLockoutMode()) {
+ Slog.v(TAG, "In lockout mode; disallowing authentication");
+ if (!mAuthClient.sendError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)) {
+ Slog.w(TAG, "Cannot send timeout message to client");
+ }
+ mAuthClient = null;
+ return;
+ }
final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
final int result = nativeAuthenticate(opId, groupId);
if (result != 0) {
@@ -354,8 +322,11 @@ public class FingerprintService extends SystemService {
}
void stopAuthentication(IBinder token) {
- if (mAuthClient == null || mAuthClient.token != token) return;
- int result = nativeStopAuthentication(mAuthClient.opId);
+ final ClientMonitor client = mAuthClient;
+ if (client == null || client.token != token) return;
+ int result = nativeStopAuthentication();
+ client.sendError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
+ removeClient(mAuthClient);
if (result != 0) {
Slog.w(TAG, "stopAuthentication failed, result=" + result);
}
@@ -363,7 +334,7 @@ public class FingerprintService extends SystemService {
void startRemove(IBinder token, int fingerId, int userId,
IFingerprintServiceReceiver receiver) {
- mRemoveClient = new ClientData(token, 0, receiver, userId);
+ mRemoveClient = new ClientMonitor(token, receiver, userId);
// The fingerprint template ids will be removed when we get confirmation from the HAL
final int result = nativeRemove(fingerId, userId);
if (result != 0) {
@@ -387,22 +358,124 @@ public class FingerprintService extends SystemService {
return result;
}
+ public boolean hasEnrolledFingerprints(int groupId) {
+ ContentResolver resolver = mContext.getContentResolver();
+ return FingerprintUtils.getFingerprintIdsForUser(resolver, groupId).length > 0;
+ }
+
void checkPermission(String permission) {
getContext().enforceCallingOrSelfPermission(permission,
"Must have " + permission + " permission.");
}
- private static final class Message {
+ private class ClientMonitor implements IBinder.DeathRecipient {
IBinder token;
- long opId;
- int groupId;
- int flags;
+ WeakReference<IFingerprintServiceReceiver> receiver;
+ int userId;
- public Message(IBinder token, long challenge, int groupId, int flags) {
+ public ClientMonitor(IBinder token, IFingerprintServiceReceiver receiver, int userId) {
this.token = token;
- this.opId = challenge;
- this.groupId = groupId;
- this.flags = flags;
+ this.receiver = new WeakReference<IFingerprintServiceReceiver>(receiver);
+ this.userId = userId;
+ try {
+ token.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "caught remote exception in linkToDeath: ", e);
+ }
+ }
+
+ public void destroy() {
+ if (token != null) {
+ token.unlinkToDeath(this, 0);
+ token = null;
+ }
+ receiver = null;
+ }
+
+ public void binderDied() {
+ token = null;
+ removeClient(this);
+ }
+
+ protected void finalize() throws Throwable {
+ try {
+ if (token != null) {
+ if (DEBUG) Slog.w(TAG, "removing leaked reference: " + token);
+ removeClient(this);
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private boolean sendRemoved(int fingerId, int groupId) {
+ IFingerprintServiceReceiver rx = receiver.get();
+ if (rx != null) {
+ try {
+ rx.onRemoved(mHalDeviceId, fingerId, groupId);
+ return true;
+ } catch (RemoteException e) {
+ if (DEBUG) Slog.v(TAG, "Failed to invoke sendRemoved:", e);
+ }
+ }
+ removeClient(this);
+ return false;
+ }
+
+ private boolean sendEnrollResult(int fpId, int groupId, int remaining) {
+ IFingerprintServiceReceiver rx = receiver.get();
+ if (rx != null) {
+ try {
+ rx.onEnrollResult(mHalDeviceId, fpId, groupId, remaining);
+ return true;
+ } catch (RemoteException e) {
+ if (DEBUG) Slog.v(TAG, "Failed to invoke sendEnrollResult:", e);
+ }
+ }
+ removeClient(this);
+ return false;
+ }
+
+ private boolean sendAuthenticated(int fpId, int groupId) {
+ IFingerprintServiceReceiver rx = receiver.get();
+ if (rx != null) {
+ try {
+ rx.onAuthenticated(mHalDeviceId, fpId, groupId);
+ return true;
+ } catch (RemoteException e) {
+ if (DEBUG) Slog.v(TAG, "Failed to invoke sendProcessed:", e);
+ }
+ }
+ removeClient(this);
+ return false;
+ }
+
+ private boolean sendAcquired(int acquiredInfo) {
+ IFingerprintServiceReceiver rx = receiver.get();
+ if (rx != null) {
+ try {
+ rx.onAcquired(mHalDeviceId, acquiredInfo);
+ return true;
+ } catch (RemoteException e) {
+ if (DEBUG) Slog.v(TAG, "Failed to invoke sendAcquired:", e);
+ }
+ }
+ removeClient(this);
+ return false;
+ }
+
+ private boolean sendError(int error) {
+ IFingerprintServiceReceiver rx = receiver.get();
+ if (rx != null) {
+ try {
+ rx.onError(mHalDeviceId, error);
+ return true;
+ } catch (RemoteException e) {
+ if (DEBUG) Slog.v(TAG, "Failed to invoke sendError:", e);
+ }
+ }
+ removeClient(this);
+ return false;
}
}
@@ -415,13 +488,14 @@ public class FingerprintService extends SystemService {
@Override
// Binder call
- public void enroll(final IBinder token, final long opid, final int groupId,
+ public void enroll(final IBinder token, final byte[] cryptoToken, final int groupId,
final IFingerprintServiceReceiver receiver, final int flags) {
checkPermission(MANAGE_FINGERPRINT);
+ final byte [] cryptoClone = Arrays.copyOf(cryptoToken, cryptoToken.length);
mHandler.post(new Runnable() {
@Override
public void run() {
- startEnrollment(token, opid, groupId, receiver, flags);
+ startEnrollment(token, cryptoClone, groupId, receiver, flags);
}
});
}
@@ -503,6 +577,13 @@ public class FingerprintService extends SystemService {
checkPermission(USE_FINGERPRINT);
return FingerprintService.this.getEnrolledFingerprints(groupId);
}
+
+ @Override
+ // Binder call
+ public boolean hasEnrolledFingerprints(int groupId) {
+ checkPermission(USE_FINGERPRINT);
+ return FingerprintService.this.hasEnrolledFingerprints(groupId);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/location/FlpHardwareProvider.java b/services/core/java/com/android/server/location/FlpHardwareProvider.java
index 834dff2..1fb22be 100644
--- a/services/core/java/com/android/server/location/FlpHardwareProvider.java
+++ b/services/core/java/com/android/server/location/FlpHardwareProvider.java
@@ -42,11 +42,13 @@ import android.util.Log;
* {@hide}
*/
public class FlpHardwareProvider {
+ private static final int FIRST_VERSION_WITH_FLUSH_LOCATIONS = 2;
private GeofenceHardwareImpl mGeofenceHardwareSink = null;
private IFusedLocationHardwareSink mLocationSink = null;
// Capabilities provided by FlpCallbacks
private boolean mHaveBatchingCapabilities;
private int mBatchingCapabilities;
+ private int mVersion;
private static FlpHardwareProvider sSingletonInstance = null;
@@ -150,6 +152,11 @@ public class FlpHardwareProvider {
}
}
+ private void setVersion(int version) {
+ mVersion = version;
+ getGeofenceHardwareSink().setVersion(version);
+ }
+
private void maybeSendCapabilities() {
IFusedLocationHardwareSink sink;
boolean haveBatchingCapabilities;
@@ -366,7 +373,12 @@ public class FlpHardwareProvider {
@Override
public void flushBatchedLocations() {
- nativeFlushBatchedLocations();
+ if (mVersion >= FIRST_VERSION_WITH_FLUSH_LOCATIONS) {
+ nativeFlushBatchedLocations();
+ } else {
+ Log.wtf(TAG,
+ "Tried to call flushBatchedLocations on an unsupported implementation");
+ }
}
@Override
@@ -388,6 +400,11 @@ public class FlpHardwareProvider {
public void injectDeviceContext(int deviceEnabledContext) {
nativeInjectDeviceContext(deviceEnabledContext);
}
+
+ @Override
+ public int getVersion() {
+ return mVersion;
+ }
};
private final IFusedGeofenceHardware mGeofenceHardwareService =
diff --git a/services/core/java/com/android/server/location/FusedLocationHardwareSecure.java b/services/core/java/com/android/server/location/FusedLocationHardwareSecure.java
index e49c411..a08d326 100644
--- a/services/core/java/com/android/server/location/FusedLocationHardwareSecure.java
+++ b/services/core/java/com/android/server/location/FusedLocationHardwareSecure.java
@@ -122,4 +122,10 @@ public class FusedLocationHardwareSecure extends IFusedLocationHardware.Stub {
checkPermissions();
mLocationHardware.flushBatchedLocations();
}
+
+ @Override
+ public int getVersion() throws RemoteException {
+ checkPermissions();
+ return mLocationHardware.getVersion();
+ }
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4cf2909..997d546 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -29,9 +29,11 @@ import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.INotificationManager;
+import android.app.INotificationManagerCallback;
import android.app.ITransientNotification;
import android.app.Notification;
import android.app.NotificationManager;
+import android.app.NotificationManager.Policy;
import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
@@ -227,6 +229,8 @@ public class NotificationManagerService extends SystemService {
new ArrayMap<String, NotificationRecord>();
final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
+ private final ArrayMap<String, Policy.Token> mPolicyTokens = new ArrayMap<>();
+
// The last key in this list owns the hardware.
ArrayList<String> mLights = new ArrayList<>();
@@ -893,6 +897,13 @@ public class NotificationManagerService extends SystemService {
updateInterruptionFilterLocked();
}
}
+
+ @Override
+ void onPolicyChanged() {
+ getContext().sendBroadcast(
+ new Intent(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED)
+ .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY));
+ }
});
final File systemDir = new File(Environment.getDataDirectory(), "system");
mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
@@ -1551,6 +1562,18 @@ public class NotificationManagerService extends SystemService {
message);
}
+ private void enforcePolicyToken(Policy.Token token, String method) {
+ if (!checkPolicyToken(token)) {
+ Slog.w(TAG, "Invalid notification policy token calling " + method);
+ throw new SecurityException("Invalid notification policy token");
+ }
+ }
+
+ private boolean checkPolicyToken(Policy.Token token) {
+ return mPolicyTokens.containsValue(token)
+ || mListeners.mPolicyTokens.containsValue(token);
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -1586,24 +1609,73 @@ public class NotificationManagerService extends SystemService {
enforceSystemOrSystemUIOrVolume("INotificationManager.isSystemConditionProviderEnabled");
return mConditionProviders.isSystemProviderEnabled(path);
}
- };
- private String[] getActiveNotificationKeys(INotificationListener token) {
- final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
- final ArrayList<String> keys = new ArrayList<String>();
- if (info.isEnabledForCurrentProfiles()) {
- synchronized (mNotificationList) {
- final int N = mNotificationList.size();
- for (int i = 0; i < N; i++) {
- final StatusBarNotification sbn = mNotificationList.get(i).sbn;
- if (info.enabledAndUserMatches(sbn.getUserId())) {
- keys.add(sbn.getKey());
+ @Override
+ public Policy.Token getPolicyTokenFromListener(INotificationListener listener) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mListeners.getPolicyToken(listener);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void requestNotificationPolicyToken(String pkg,
+ INotificationManagerCallback callback) throws RemoteException {
+ if (callback == null) {
+ Slog.w(TAG, "requestNotificationPolicyToken: no callback specified");
+ return;
+ }
+ if (pkg == null) {
+ Slog.w(TAG, "requestNotificationPolicyToken denied: no package specified");
+ callback.onPolicyToken(null);
+ return;
+ }
+ Policy.Token token = null;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mNotificationList) {
+ token = mPolicyTokens.get(pkg);
+ if (token == null) {
+ token = new Policy.Token(new Binder());
+ mPolicyTokens.put(pkg, token);
}
+ if (DBG) Slog.w(TAG, "requestNotificationPolicyToken granted for " + pkg);
}
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
+ callback.onPolicyToken(token);
+ }
+
+ @Override
+ public boolean isNotificationPolicyTokenValid(String pkg, Policy.Token token) {
+ return checkPolicyToken(token);
}
- return keys.toArray(new String[keys.size()]);
- }
+
+ @Override
+ public Policy getNotificationPolicy(Policy.Token token) {
+ enforcePolicyToken(token, "getNotificationPolicy");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mZenModeHelper.getNotificationPolicy();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void setNotificationPolicy(Policy.Token token, Policy policy) {
+ enforcePolicyToken(token, "setNotificationPolicy");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mZenModeHelper.setNotificationPolicy(policy);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ };
private String disableNotificationEffects(NotificationRecord record) {
if (mDisableNotificationEffects) {
@@ -1718,6 +1790,10 @@ public class NotificationManagerService extends SystemService {
pw.print(listener.component);
}
pw.println(')');
+ pw.print(" mPolicyTokens.keys: ");
+ pw.println(TextUtils.join(",", mPolicyTokens.keySet()));
+ pw.print(" mListeners.mPolicyTokens.keys: ");
+ pw.println(TextUtils.join(",", mListeners.mPolicyTokens.keySet()));
}
pw.println("\n Condition providers:");
@@ -2970,12 +3046,18 @@ public class NotificationManagerService extends SystemService {
public class NotificationListeners extends ManagedServices {
private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
+ private final ArrayMap<ComponentName, Policy.Token> mPolicyTokens = new ArrayMap<>();
private boolean mNotificationGroupsDesired;
public NotificationListeners() {
super(getContext(), mHandler, mNotificationList, mUserProfiles);
}
+ public Policy.Token getPolicyToken(INotificationListener listener) {
+ final ManagedServiceInfo info = checkServiceTokenLocked(listener);
+ return info == null ? null : mPolicyTokens.get(info.component);
+ }
+
@Override
protected Config getConfig() {
Config c = new Config();
@@ -3000,6 +3082,7 @@ public class NotificationManagerService extends SystemService {
synchronized (mNotificationList) {
updateNotificationGroupsDesiredLocked();
update = makeRankingUpdateLocked(info);
+ mPolicyTokens.put(info.component, new Policy.Token(new Binder()));
}
try {
listener.onListenerConnected(update);
@@ -3016,6 +3099,7 @@ public class NotificationManagerService extends SystemService {
}
mLightTrimListeners.remove(removed);
updateNotificationGroupsDesiredLocked();
+ mPolicyTokens.remove(removed.component);
}
public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 40218bb..9cb8af5 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -21,6 +21,7 @@ import static android.media.AudioAttributes.USAGE_NOTIFICATION;
import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
import android.app.AppOpsManager;
+import android.app.NotificationManager.Policy;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -57,6 +58,7 @@ import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Objects;
/**
* NotificationManagerService helper for functionality related to zen mode.
@@ -230,6 +232,21 @@ public class ZenModeHelper {
mConfig.writeXml(out);
}
+ public Policy getNotificationPolicy() {
+ return getNotificationPolicy(mConfig);
+ }
+
+ private static Policy getNotificationPolicy(ZenModeConfig config) {
+ return config == null ? null : config.toNotificationPolicy();
+ }
+
+ public void setNotificationPolicy(Policy policy) {
+ if (policy == null || mConfig == null) return;
+ final ZenModeConfig newConfig = mConfig.copy();
+ newConfig.applyNotificationPolicy(policy);
+ setConfig(newConfig, "setNotificationPolicy");
+ }
+
public ZenModeConfig getConfig() {
return mConfig;
}
@@ -247,8 +264,13 @@ public class ZenModeHelper {
if (config.equals(mConfig)) return true;
if (DEBUG) Log.d(TAG, "setConfig reason=" + reason);
ZenLog.traceConfig(mConfig, config);
+ final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig),
+ getNotificationPolicy(config));
mConfig = config;
dispatchOnConfigChanged();
+ if (policyChanged){
+ dispatchOnPolicyChanged();
+ }
final String val = Integer.toString(mConfig.hashCode());
Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
if (!evaluateZenMode(reason, setRingerMode)) {
@@ -355,6 +377,12 @@ public class ZenModeHelper {
}
}
+ private void dispatchOnPolicyChanged() {
+ for (Callback callback : mCallbacks) {
+ callback.onPolicyChanged();
+ }
+ }
+
private void dispatchOnZenModeChanged() {
for (Callback callback : mCallbacks) {
callback.onZenModeChanged();
@@ -617,6 +645,7 @@ public class ZenModeHelper {
public static class Callback {
void onConfigChanged() {}
void onZenModeChanged() {}
+ void onPolicyChanged() {}
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 936840a..fce01e5 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -183,6 +183,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
static final int APPLICATION_PANEL_SUBLAYER = 1;
static final int APPLICATION_SUB_PANEL_SUBLAYER = 2;
+ static final int APPLICATION_ABOVE_SUB_PANEL_SUBLAYER = 3;
static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
@@ -2015,6 +2016,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return APPLICATION_MEDIA_OVERLAY_SUBLAYER;
case TYPE_APPLICATION_SUB_PANEL:
return APPLICATION_SUB_PANEL_SUBLAYER;
+ case TYPE_APPLICATION_ABOVE_SUB_PANEL:
+ return APPLICATION_ABOVE_SUB_PANEL_SUBLAYER;
}
Log.e(TAG, "Unknown sub-window type: " + type);
return 0;
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 75c33af..1a52933 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -145,6 +145,9 @@ public class KeyguardServiceDelegate {
if (mKeyguardState.bootCompleted) {
mKeyguardService.onBootCompleted();
}
+ if (mKeyguardState.occluded) {
+ mKeyguardService.setOccluded(mKeyguardState.occluded);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index f6df757..184224b 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -79,7 +79,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
public void binderDied() {
Slog.i(TAG, "binder died for pkg=" + pkg);
- disableInternal(userId, 0, token, pkg);
+ disableForUser(0, token, pkg, userId);
token.unlinkToDeath(this, 0);
}
}
@@ -194,10 +194,11 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
@Override
public void disable(int what, IBinder token, String pkg) {
- disableInternal(mCurrentUserId, what, token, pkg);
+ disableForUser(what, token, pkg, mCurrentUserId);
}
- private void disableInternal(int userId, int what, IBinder token, String pkg) {
+ @Override
+ public void disableForUser(int what, IBinder token, String pkg, int userId) {
enforceStatusBar();
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java b/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
index 1135dfe..8fc979c 100644
--- a/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
@@ -16,29 +16,21 @@
package com.android.server.updates;
+import com.android.server.EventLogTags;
+
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
-import android.provider.Settings;
-import android.util.Base64;
import android.util.EventLog;
import android.util.Slog;
-import com.android.server.EventLogTags;
-
-import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
-import java.io.InputStream;
import java.io.IOException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
+import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
-import java.security.Signature;
import libcore.io.IoUtils;
import libcore.io.Streams;
@@ -48,11 +40,8 @@ public class ConfigUpdateInstallReceiver extends BroadcastReceiver {
private static final String TAG = "ConfigUpdateInstallReceiver";
private static final String EXTRA_REQUIRED_HASH = "REQUIRED_HASH";
- private static final String EXTRA_SIGNATURE = "SIGNATURE";
private static final String EXTRA_VERSION_NUMBER = "VERSION";
- private static final String UPDATE_CERTIFICATE_KEY = "config_update_certificate";
-
protected final File updateDir;
protected final File updateContent;
protected final File updateVersion;
@@ -71,16 +60,12 @@ public class ConfigUpdateInstallReceiver extends BroadcastReceiver {
@Override
public void run() {
try {
- // get the certificate from Settings.Secure
- X509Certificate cert = getCert(context.getContentResolver());
// get the content path from the extras
byte[] altContent = getAltContent(context, intent);
// get the version from the extras
int altVersion = getVersionFromIntent(intent);
// get the previous value from the extras
String altRequiredHash = getRequiredHashFromIntent(intent);
- // get the signature from the extras
- String altSig = getSignatureFromIntent(intent);
// get the version currently being used
int currentVersion = getCurrentVersion();
// get the hash of the currently used value
@@ -90,10 +75,6 @@ public class ConfigUpdateInstallReceiver extends BroadcastReceiver {
} else if (!verifyPreviousHash(currentHash, altRequiredHash)) {
EventLog.writeEvent(EventLogTags.CONFIG_INSTALL_FAILED,
"Current hash did not match required value");
- } else if (!verifySignature(altContent, altVersion, altRequiredHash, altSig,
- cert)) {
- EventLog.writeEvent(EventLogTags.CONFIG_INSTALL_FAILED,
- "Signature did not verify");
} else {
// install the new content
Slog.i(TAG, "Found new update, installing...");
@@ -114,20 +95,6 @@ public class ConfigUpdateInstallReceiver extends BroadcastReceiver {
}.start();
}
- private X509Certificate getCert(ContentResolver cr) {
- // get the cert from settings
- String cert = Settings.Secure.getString(cr, UPDATE_CERTIFICATE_KEY);
- // convert it into a real certificate
- try {
- byte[] derCert = Base64.decode(cert.getBytes(), Base64.DEFAULT);
- InputStream istream = new ByteArrayInputStream(derCert);
- CertificateFactory cf = CertificateFactory.getInstance("X.509");
- return (X509Certificate) cf.generateCertificate(istream);
- } catch (CertificateException e) {
- throw new IllegalStateException("Got malformed certificate from settings, ignoring");
- }
- }
-
private Uri getContentFromIntent(Intent i) {
Uri data = i.getData();
if (data == null) {
@@ -152,14 +119,6 @@ public class ConfigUpdateInstallReceiver extends BroadcastReceiver {
return extraValue.trim();
}
- private String getSignatureFromIntent(Intent i) {
- String extraValue = i.getStringExtra(EXTRA_SIGNATURE);
- if (extraValue == null) {
- throw new IllegalStateException("Missing required signature, ignoring.");
- }
- return extraValue.trim();
- }
-
private int getCurrentVersion() throws NumberFormatException {
try {
String strVersion = IoUtils.readFileAsString(updateVersion.getCanonicalPath()).trim();
@@ -215,16 +174,6 @@ public class ConfigUpdateInstallReceiver extends BroadcastReceiver {
return current.equals(required);
}
- private boolean verifySignature(byte[] content, int version, String requiredPrevious,
- String signature, X509Certificate cert) throws Exception {
- Signature signer = Signature.getInstance("SHA512withRSA");
- signer.initVerify(cert);
- signer.update(content);
- signer.update(Long.toString(version).getBytes());
- signer.update(requiredPrevious.getBytes());
- return signer.verify(Base64.decode(signature.getBytes(), Base64.DEFAULT));
- }
-
protected void writeUpdate(File dir, File file, byte[] content) throws IOException {
FileOutputStream out = null;
File tmp = null;
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index ae8832a..91ce739 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -339,6 +339,7 @@ final class AccessibilityController {
case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL:
case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
case WindowManager.LayoutParams.TYPE_PHONE:
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 55dd911..bb53534 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -148,7 +148,7 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
if (timeout >= 0) {
// The activity manager declined to abort dispatching.
// Wait a bit longer and timeout again later.
- return timeout;
+ return timeout * 1000000L; // nanoseconds
}
} catch (RemoteException ex) {
}