Merge "Implement IntentFilter verification service."
diff --git a/api/current.txt b/api/current.txt
index d79ccbe..bf55b7c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1696,6 +1696,8 @@
public static final class R.id {
ctor public R.id();
+ field public static final int accessibilityActionScrollToPosition = 16908342; // 0x1020036
+ field public static final int accessibilityActionShowOnScreen = 16908341; // 0x1020035
field public static final int addToDictionary = 16908330; // 0x102002a
field public static final int background = 16908288; // 0x1020000
field public static final int button1 = 16908313; // 0x1020019
@@ -3553,6 +3555,7 @@
public class ActivityManager {
method public int addAppTask(android.app.Activity, android.content.Intent, android.app.ActivityManager.TaskDescription, android.graphics.Bitmap);
method public boolean clearApplicationUserData();
+ method public void clearWatchHeapLimit();
method public void dumpPackageState(java.io.FileDescriptor, java.lang.String);
method public android.util.Size getAppTaskThumbnailSize();
method public java.util.List<android.app.ActivityManager.AppTask> getAppTasks();
@@ -3579,6 +3582,8 @@
method public void moveTaskToFront(int, int);
method public void moveTaskToFront(int, int, android.os.Bundle);
method public deprecated void restartPackage(java.lang.String);
+ method public void setWatchHeapLimit(long);
+ field public static final java.lang.String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT";
field public static final int LOCK_TASK_MODE_LOCKED = 1; // 0x1
field public static final int LOCK_TASK_MODE_NONE = 0; // 0x0
field public static final int LOCK_TASK_MODE_PINNED = 2; // 0x2
@@ -5434,10 +5439,12 @@
public static final class VoiceInteractor.PickOptionRequest.Option implements android.os.Parcelable {
ctor public VoiceInteractor.PickOptionRequest.Option(java.lang.CharSequence);
+ ctor public VoiceInteractor.PickOptionRequest.Option(java.lang.CharSequence, int);
method public android.app.VoiceInteractor.PickOptionRequest.Option addSynonym(java.lang.CharSequence);
method public int countSynonyms();
method public int describeContents();
method public android.os.Bundle getExtras();
+ method public int getIndex();
method public java.lang.CharSequence getLabel();
method public java.lang.CharSequence getSynonymAt(int);
method public void setExtras(android.os.Bundle);
@@ -5707,6 +5714,7 @@
field public static final java.lang.String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED";
field public static final java.lang.String EXTRA_PROVISIONING_LOCALE = "android.app.extra.PROVISIONING_LOCALE";
field public static final java.lang.String EXTRA_PROVISIONING_LOCAL_TIME = "android.app.extra.PROVISIONING_LOCAL_TIME";
+ field public static final java.lang.String EXTRA_PROVISIONING_RESET_PROTECTION_PARAMETERS = "android.app.extra.PROVISIONING_RESET_PROTECTION_PARAMETERS";
field public static final java.lang.String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
field public static final java.lang.String EXTRA_PROVISIONING_TIME_ZONE = "android.app.extra.PROVISIONING_TIME_ZONE";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_HIDDEN = "android.app.extra.PROVISIONING_WIFI_HIDDEN";
@@ -8009,6 +8017,7 @@
field public static final java.lang.String ACTION_POWER_CONNECTED = "android.intent.action.ACTION_POWER_CONNECTED";
field public static final java.lang.String ACTION_POWER_DISCONNECTED = "android.intent.action.ACTION_POWER_DISCONNECTED";
field public static final java.lang.String ACTION_POWER_USAGE_SUMMARY = "android.intent.action.POWER_USAGE_SUMMARY";
+ field public static final java.lang.String ACTION_PROCESS_TEXT = "android.intent.action.PROCESS_TEXT";
field public static final java.lang.String ACTION_PROVIDER_CHANGED = "android.intent.action.PROVIDER_CHANGED";
field public static final java.lang.String ACTION_QUICK_CLOCK = "android.intent.action.QUICK_CLOCK";
field public static final java.lang.String ACTION_REBOOT = "android.intent.action.REBOOT";
@@ -8109,6 +8118,8 @@
field public static final java.lang.String EXTRA_NOT_UNKNOWN_SOURCE = "android.intent.extra.NOT_UNKNOWN_SOURCE";
field public static final java.lang.String EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI";
field public static final java.lang.String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
+ field public static final java.lang.String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT";
+ field public static final java.lang.String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY";
field public static final java.lang.String EXTRA_REFERRER = "android.intent.extra.REFERRER";
field public static final java.lang.String EXTRA_REFERRER_NAME = "android.intent.extra.REFERRER_NAME";
field public static final java.lang.String EXTRA_REMOTE_INTENT_TOKEN = "android.intent.extra.remote_intent_token";
@@ -24143,8 +24154,16 @@
field public static final java.lang.String ACTION_SET_ALARM = "android.intent.action.SET_ALARM";
field public static final java.lang.String ACTION_SET_TIMER = "android.intent.action.SET_TIMER";
field public static final java.lang.String ACTION_SHOW_ALARMS = "android.intent.action.SHOW_ALARMS";
+ field public static final java.lang.String ACTION_VOICE_CANCEL_ALARM = "android.intent.action.VOICE_CANCEL_ALARM";
+ field public static final java.lang.String ACTION_VOICE_DELETE_ALARM = "android.intent.action.VOICE_DELETE_ALARM";
+ field public static final java.lang.String ALARM_SEARCH_MODE_ALL = "all";
+ field public static final java.lang.String ALARM_SEARCH_MODE_NEXT = "next";
+ field public static final java.lang.String ALARM_SEARCH_MODE_NONE = "none";
+ field public static final java.lang.String ALARM_SEARCH_MODE_TIME = "time";
+ field public static final java.lang.String EXTRA_ALARM_SEARCH_MODE = "android.intent.extra.alarm.ALARM_SEARCH_MODE";
field public static final java.lang.String EXTRA_DAYS = "android.intent.extra.alarm.DAYS";
field public static final java.lang.String EXTRA_HOUR = "android.intent.extra.alarm.HOUR";
+ field public static final java.lang.String EXTRA_IS_PM = "android.intent.extra.alarm.IS_PM";
field public static final java.lang.String EXTRA_LENGTH = "android.intent.extra.alarm.LENGTH";
field public static final java.lang.String EXTRA_MESSAGE = "android.intent.extra.alarm.MESSAGE";
field public static final java.lang.String EXTRA_MINUTES = "android.intent.extra.alarm.MINUTES";
@@ -25823,6 +25842,8 @@
method public static java.lang.String getVersion(android.content.Context);
field public static final java.lang.String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
field public static final java.lang.String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE";
+ field public static final java.lang.String ACTION_STILL_IMAGE_CAMERA_COOLDOWN = "android.media.action.STILL_IMAGE_CAMERA_COOLDOWN";
+ field public static final java.lang.String ACTION_STILL_IMAGE_CAMERA_PREWARM = "android.media.action.STILL_IMAGE_CAMERA_PREWARM";
field public static final java.lang.String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE";
field public static final java.lang.String AUTHORITY = "media";
field public static final java.lang.String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit";
@@ -27618,7 +27639,7 @@
method public android.renderscript.ScriptGroup2.Closure addInvoke(android.renderscript.Script.InvokeID, java.lang.Object...);
method public android.renderscript.ScriptGroup2.Closure addKernel(android.renderscript.Script.KernelID, android.renderscript.Type, java.lang.Object[], java.util.Map<android.renderscript.Script.FieldID, java.lang.Object>);
method public android.renderscript.ScriptGroup2.Closure addKernel(android.renderscript.Script.KernelID, android.renderscript.Type, java.lang.Object...);
- method public android.renderscript.ScriptGroup2 create(android.renderscript.ScriptGroup2.Future...);
+ method public android.renderscript.ScriptGroup2 create(java.lang.String, android.renderscript.ScriptGroup2.Future...);
}
public static class ScriptGroup2.Closure extends android.renderscript.BaseObj {
@@ -28271,6 +28292,7 @@
ctor public VoiceInteractionSession(android.content.Context);
ctor public VoiceInteractionSession(android.content.Context, android.os.Handler);
method public void finish();
+ method public android.content.Context getContext();
method public android.view.LayoutInflater getLayoutInflater();
method public android.app.Dialog getWindow();
method public void hide();
@@ -36383,9 +36405,11 @@
method public void setVisibleToUser(boolean);
method public void writeToParcel(android.os.Parcel, int);
field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
+ field public static final java.lang.String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
field public static final java.lang.String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
field public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
field public static final java.lang.String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
+ field public static final java.lang.String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
field public static final java.lang.String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
field public static final java.lang.String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT";
field public static final java.lang.String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
@@ -36443,6 +36467,7 @@
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_PREVIOUS_HTML_ELEMENT;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_BACKWARD;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_FORWARD;
+ field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_TO_POSITION;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SELECT;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_SELECTION;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_TEXT;
diff --git a/api/system-current.txt b/api/system-current.txt
index e57eee5..1152de5 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1773,6 +1773,8 @@
public static final class R.id {
ctor public R.id();
+ field public static final int accessibilityActionScrollToPosition = 16908342; // 0x1020036
+ field public static final int accessibilityActionShowOnScreen = 16908341; // 0x1020035
field public static final int addToDictionary = 16908330; // 0x102002a
field public static final int background = 16908288; // 0x1020000
field public static final int button1 = 16908313; // 0x1020019
@@ -3641,6 +3643,7 @@
public class ActivityManager {
method public int addAppTask(android.app.Activity, android.content.Intent, android.app.ActivityManager.TaskDescription, android.graphics.Bitmap);
method public boolean clearApplicationUserData();
+ method public void clearWatchHeapLimit();
method public void dumpPackageState(java.io.FileDescriptor, java.lang.String);
method public android.util.Size getAppTaskThumbnailSize();
method public java.util.List<android.app.ActivityManager.AppTask> getAppTasks();
@@ -3668,6 +3671,8 @@
method public void moveTaskToFront(int, int);
method public void moveTaskToFront(int, int, android.os.Bundle);
method public deprecated void restartPackage(java.lang.String);
+ method public void setWatchHeapLimit(long);
+ field public static final java.lang.String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT";
field public static final int LOCK_TASK_MODE_LOCKED = 1; // 0x1
field public static final int LOCK_TASK_MODE_NONE = 0; // 0x0
field public static final int LOCK_TASK_MODE_PINNED = 2; // 0x2
@@ -5525,10 +5530,12 @@
public static final class VoiceInteractor.PickOptionRequest.Option implements android.os.Parcelable {
ctor public VoiceInteractor.PickOptionRequest.Option(java.lang.CharSequence);
+ ctor public VoiceInteractor.PickOptionRequest.Option(java.lang.CharSequence, int);
method public android.app.VoiceInteractor.PickOptionRequest.Option addSynonym(java.lang.CharSequence);
method public int countSynonyms();
method public int describeContents();
method public android.os.Bundle getExtras();
+ method public int getIndex();
method public java.lang.CharSequence getLabel();
method public java.lang.CharSequence getSynonymAt(int);
method public void setExtras(android.os.Bundle);
@@ -5813,6 +5820,7 @@
field public static final java.lang.String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED";
field public static final java.lang.String EXTRA_PROVISIONING_LOCALE = "android.app.extra.PROVISIONING_LOCALE";
field public static final java.lang.String EXTRA_PROVISIONING_LOCAL_TIME = "android.app.extra.PROVISIONING_LOCAL_TIME";
+ field public static final java.lang.String EXTRA_PROVISIONING_RESET_PROTECTION_PARAMETERS = "android.app.extra.PROVISIONING_RESET_PROTECTION_PARAMETERS";
field public static final java.lang.String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
field public static final java.lang.String EXTRA_PROVISIONING_TIME_ZONE = "android.app.extra.PROVISIONING_TIME_ZONE";
field public static final java.lang.String EXTRA_PROVISIONING_WIFI_HIDDEN = "android.app.extra.PROVISIONING_WIFI_HIDDEN";
@@ -8229,6 +8237,7 @@
field public static final java.lang.String ACTION_POWER_CONNECTED = "android.intent.action.ACTION_POWER_CONNECTED";
field public static final java.lang.String ACTION_POWER_DISCONNECTED = "android.intent.action.ACTION_POWER_DISCONNECTED";
field public static final java.lang.String ACTION_POWER_USAGE_SUMMARY = "android.intent.action.POWER_USAGE_SUMMARY";
+ field public static final java.lang.String ACTION_PROCESS_TEXT = "android.intent.action.PROCESS_TEXT";
field public static final java.lang.String ACTION_PROVIDER_CHANGED = "android.intent.action.PROVIDER_CHANGED";
field public static final java.lang.String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
field public static final java.lang.String ACTION_QUICK_CLOCK = "android.intent.action.QUICK_CLOCK";
@@ -8334,6 +8343,8 @@
field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
field public static final java.lang.String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
field public static final java.lang.String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
+ field public static final java.lang.String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT";
+ field public static final java.lang.String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY";
field public static final java.lang.String EXTRA_REFERRER = "android.intent.extra.REFERRER";
field public static final java.lang.String EXTRA_REFERRER_NAME = "android.intent.extra.REFERRER_NAME";
field public static final java.lang.String EXTRA_REMOTE_INTENT_TOKEN = "android.intent.extra.remote_intent_token";
@@ -26026,8 +26037,16 @@
field public static final java.lang.String ACTION_SET_ALARM = "android.intent.action.SET_ALARM";
field public static final java.lang.String ACTION_SET_TIMER = "android.intent.action.SET_TIMER";
field public static final java.lang.String ACTION_SHOW_ALARMS = "android.intent.action.SHOW_ALARMS";
+ field public static final java.lang.String ACTION_VOICE_CANCEL_ALARM = "android.intent.action.VOICE_CANCEL_ALARM";
+ field public static final java.lang.String ACTION_VOICE_DELETE_ALARM = "android.intent.action.VOICE_DELETE_ALARM";
+ field public static final java.lang.String ALARM_SEARCH_MODE_ALL = "all";
+ field public static final java.lang.String ALARM_SEARCH_MODE_NEXT = "next";
+ field public static final java.lang.String ALARM_SEARCH_MODE_NONE = "none";
+ field public static final java.lang.String ALARM_SEARCH_MODE_TIME = "time";
+ field public static final java.lang.String EXTRA_ALARM_SEARCH_MODE = "android.intent.extra.alarm.ALARM_SEARCH_MODE";
field public static final java.lang.String EXTRA_DAYS = "android.intent.extra.alarm.DAYS";
field public static final java.lang.String EXTRA_HOUR = "android.intent.extra.alarm.HOUR";
+ field public static final java.lang.String EXTRA_IS_PM = "android.intent.extra.alarm.IS_PM";
field public static final java.lang.String EXTRA_LENGTH = "android.intent.extra.alarm.LENGTH";
field public static final java.lang.String EXTRA_MESSAGE = "android.intent.extra.alarm.MESSAGE";
field public static final java.lang.String EXTRA_MINUTES = "android.intent.extra.alarm.MINUTES";
@@ -27706,6 +27725,8 @@
method public static java.lang.String getVersion(android.content.Context);
field public static final java.lang.String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
field public static final java.lang.String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE";
+ field public static final java.lang.String ACTION_STILL_IMAGE_CAMERA_COOLDOWN = "android.media.action.STILL_IMAGE_CAMERA_COOLDOWN";
+ field public static final java.lang.String ACTION_STILL_IMAGE_CAMERA_PREWARM = "android.media.action.STILL_IMAGE_CAMERA_PREWARM";
field public static final java.lang.String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE";
field public static final java.lang.String AUTHORITY = "media";
field public static final java.lang.String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit";
@@ -29604,7 +29625,7 @@
method public android.renderscript.ScriptGroup2.Closure addInvoke(android.renderscript.Script.InvokeID, java.lang.Object...);
method public android.renderscript.ScriptGroup2.Closure addKernel(android.renderscript.Script.KernelID, android.renderscript.Type, java.lang.Object[], java.util.Map<android.renderscript.Script.FieldID, java.lang.Object>);
method public android.renderscript.ScriptGroup2.Closure addKernel(android.renderscript.Script.KernelID, android.renderscript.Type, java.lang.Object...);
- method public android.renderscript.ScriptGroup2 create(android.renderscript.ScriptGroup2.Future...);
+ method public android.renderscript.ScriptGroup2 create(java.lang.String, android.renderscript.ScriptGroup2.Future...);
}
public static class ScriptGroup2.Closure extends android.renderscript.BaseObj {
@@ -30346,6 +30367,7 @@
ctor public VoiceInteractionSession(android.content.Context);
ctor public VoiceInteractionSession(android.content.Context, android.os.Handler);
method public void finish();
+ method public android.content.Context getContext();
method public android.view.LayoutInflater getLayoutInflater();
method public android.app.Dialog getWindow();
method public void hide();
@@ -38935,9 +38957,11 @@
method public void setVisibleToUser(boolean);
method public void writeToParcel(android.os.Parcel, int);
field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
+ field public static final java.lang.String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
field public static final java.lang.String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
field public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
field public static final java.lang.String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
+ field public static final java.lang.String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
field public static final java.lang.String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
field public static final java.lang.String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT";
field public static final java.lang.String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
@@ -38995,6 +39019,7 @@
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_PREVIOUS_HTML_ELEMENT;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_BACKWARD;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_FORWARD;
+ field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_TO_POSITION;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SELECT;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_SELECTION;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_TEXT;
@@ -40847,6 +40872,7 @@
method public abstract boolean onKeyUp(int, android.view.KeyEvent);
method public abstract void onMeasure(int, int);
method public abstract void onOverScrolled(int, int, boolean, boolean);
+ method public abstract void onProvideVirtualAssistStructure(android.view.ViewAssistStructure);
method public abstract void onScrollChanged(int, int, int, int);
method public abstract void onSizeChanged(int, int, int, int);
method public abstract void onStartTemporaryDetach();
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 0a53371..908d46e 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -1189,12 +1189,12 @@
private void runSetWatchHeap() throws Exception {
String proc = nextArgRequired();
String limit = nextArgRequired();
- mAm.setDumpHeapDebugLimit(proc, Long.parseLong(limit));
+ mAm.setDumpHeapDebugLimit(proc, 0, Long.parseLong(limit), null);
}
private void runClearWatchHeap() throws Exception {
String proc = nextArgRequired();
- mAm.setDumpHeapDebugLimit(proc, -1);
+ mAm.setDumpHeapDebugLimit(proc, 0, -1, null);
}
private void runBugReport() throws Exception {
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 89dd079..b43c462 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -30,6 +30,7 @@
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageManager;
+import android.content.pm.IPackageMoveObserver;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
@@ -55,12 +56,12 @@
import android.text.TextUtils;
import android.util.Log;
+import libcore.io.IoUtils;
+
import com.android.internal.content.PackageHelper;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.SizedInputStream;
-import libcore.io.IoUtils;
-
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -234,6 +235,10 @@
return runForceDexOpt();
}
+ if ("move".equals(op)) {
+ return runMove();
+ }
+
try {
if (args.length == 1) {
if (args[0].equalsIgnoreCase("-l")) {
@@ -1278,6 +1283,51 @@
}
}
+ class LocalPackageMoveObserver extends IPackageMoveObserver.Stub {
+ boolean finished;
+ int returnCode;
+
+ @Override
+ public void packageMoved(String packageName, int returnCode) throws RemoteException {
+ synchronized (this) {
+ this.finished = true;
+ this.returnCode = returnCode;
+ notifyAll();
+ }
+ }
+ }
+
+ public int runMove() {
+ final String packageName = nextArg();
+ String volumeUuid = nextArg();
+ if ("internal".equals(volumeUuid)) {
+ volumeUuid = null;
+ }
+
+ final LocalPackageMoveObserver obs = new LocalPackageMoveObserver();
+ try {
+ mPm.movePackageAndData(packageName, volumeUuid, obs);
+
+ synchronized (obs) {
+ while (!obs.finished) {
+ try {
+ obs.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ if (obs.returnCode == PackageManager.MOVE_SUCCEEDED) {
+ System.out.println("Success");
+ return 0;
+ } else {
+ System.err.println("Failure [" + obs.returnCode + "]");
+ return 1;
+ }
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
private int runUninstall() throws RemoteException {
int flags = 0;
int userId = UserHandle.USER_ALL;
@@ -1335,7 +1385,8 @@
}
final LocalIntentReceiver receiver = new LocalIntentReceiver();
- mInstaller.uninstall(pkg, flags, receiver.getIntentSender(), userId);
+ mInstaller.uninstall(pkg, null /* callerPackageName */, flags,
+ receiver.getIntentSender(), userId);
final Intent result = receiver.getResult();
final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
@@ -1813,13 +1864,14 @@
System.err.println(" pm list users");
System.err.println(" pm path PACKAGE");
System.err.println(" pm dump PACKAGE");
- System.err.println(" pm install [-lrtsfd] [-i PACKAGE] [PATH]");
+ System.err.println(" pm install [-lrtsfd] [-i PACKAGE] [--user USER_ID] [PATH]");
System.err.println(" pm install-create [-lrtsfdp] [-i PACKAGE] [-S BYTES]");
System.err.println(" pm install-write [-S BYTES] SESSION_ID SPLIT_NAME [PATH]");
System.err.println(" pm install-commit SESSION_ID");
System.err.println(" pm install-abandon SESSION_ID");
System.err.println(" pm uninstall [-k] [--user USER_ID] PACKAGE");
System.err.println(" pm set-installer PACKAGE INSTALLER");
+ System.err.println(" pm move PACKAGE [internal|UUID]");
System.err.println(" pm clear [--user USER_ID] PACKAGE");
System.err.println(" pm enable [--user USER_ID] PACKAGE_OR_COMPONENT");
System.err.println(" pm disable [--user USER_ID] PACKAGE_OR_COMPONENT");
diff --git a/core/java/android/animation/TimeAnimator.java b/core/java/android/animation/TimeAnimator.java
index 1738ade..1ba68df 100644
--- a/core/java/android/animation/TimeAnimator.java
+++ b/core/java/android/animation/TimeAnimator.java
@@ -51,6 +51,7 @@
public void setCurrentPlayTime(long playTime) {
long currentTime = AnimationUtils.currentAnimationTimeMillis();
mStartTime = Math.max(mStartTime, currentTime - playTime);
+ mStartTimeCommitted = true; // do not allow start time to be compensated for jank
animationFrame(currentTime);
}
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 85dc832..2386007 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -20,6 +20,7 @@
import android.os.Looper;
import android.os.Trace;
import android.util.AndroidRuntimeException;
+import android.util.Log;
import android.view.Choreographer;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AnimationUtils;
@@ -64,6 +65,8 @@
*/
@SuppressWarnings("unchecked")
public class ValueAnimator extends Animator {
+ private static final String TAG = "ValueAnimator";
+ private static final boolean DEBUG = false;
/**
* Internal constants
@@ -85,12 +88,30 @@
* to clone() to make deep copies of them.
*/
- // The first time that the animation's animateFrame() method is called. This time is used to
- // determine elapsed time (and therefore the elapsed fraction) in subsequent calls
- // to animateFrame()
+ /**
+ * The first time that the animation's animateFrame() method is called. This time is used to
+ * determine elapsed time (and therefore the elapsed fraction) in subsequent calls
+ * to animateFrame().
+ *
+ * Whenever mStartTime is set, you must also update mStartTimeCommitted.
+ */
long mStartTime;
/**
+ * When true, the start time has been firmly committed as a chosen reference point in
+ * time by which the progress of the animation will be evaluated. When false, the
+ * start time may be updated when the first animation frame is committed so as
+ * to compensate for jank that may have occurred between when the start time was
+ * initialized and when the frame was actually drawn.
+ *
+ * This flag is generally set to false during the first frame of the animation
+ * when the animation playing state transitions from STOPPED to RUNNING or
+ * resumes after having been paused. This flag is set to true when the start time
+ * is firmly committed and should not be further compensated for jank.
+ */
+ boolean mStartTimeCommitted;
+
+ /**
* Set when setCurrentPlayTime() is called. If negative, animation is not currently seeked
* to a value.
*/
@@ -528,6 +549,7 @@
* value makes it easier to compose statements together that construct and then set the
* duration, as in <code>ValueAnimator.ofInt(0, 10).setDuration(500).start()</code>.
*/
+ @Override
public ValueAnimator setDuration(long duration) {
if (duration < 0) {
throw new IllegalArgumentException("Animators cannot have negative duration: " +
@@ -547,6 +569,7 @@
*
* @return The length of the animation, in milliseconds.
*/
+ @Override
public long getDuration() {
return mUnscaledDuration;
}
@@ -608,6 +631,7 @@
long seekTime = (long) (mDuration * fraction);
long currentTime = AnimationUtils.currentAnimationTimeMillis();
mStartTime = currentTime - seekTime;
+ mStartTimeCommitted = true; // do not allow start time to be compensated for jank
if (mPlayingState != RUNNING) {
mSeekFraction = fraction;
mPlayingState = SEEKED;
@@ -644,7 +668,7 @@
* @hide
*/
@SuppressWarnings("unchecked")
- protected static class AnimationHandler implements Runnable {
+ protected static class AnimationHandler {
// The per-thread list of all active animations
/** @hide */
protected final ArrayList<ValueAnimator> mAnimations = new ArrayList<ValueAnimator>();
@@ -667,6 +691,7 @@
private final Choreographer mChoreographer;
private boolean mAnimationScheduled;
+ private long mLastFrameTime;
private AnimationHandler() {
mChoreographer = Choreographer.getInstance();
@@ -679,7 +704,9 @@
scheduleAnimation();
}
- private void doAnimationFrame(long frameTime) {
+ void doAnimationFrame(long frameTime) {
+ mLastFrameTime = frameTime;
+
// mPendingAnimations holds any animations that have requested to be started
// We're going to clear mPendingAnimations, but starting animation may
// cause more to be added to the pending list (for example, if one animation
@@ -700,6 +727,7 @@
}
}
}
+
// Next, process animations currently sitting on the delayed queue, adding
// them to the active animations if they are ready
int numDelayedAnims = mDelayedAnims.size();
@@ -740,6 +768,9 @@
mEndingAnims.clear();
}
+ // Schedule final commit for the frame.
+ mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, mCommit, null);
+
// If there are still active or delayed animations, schedule a future call to
// onAnimate to process the next frame of the animations.
if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
@@ -747,19 +778,37 @@
}
}
- // Called by the Choreographer.
- @Override
- public void run() {
- mAnimationScheduled = false;
- doAnimationFrame(mChoreographer.getFrameTime());
+ void commitAnimationFrame(long frameTime) {
+ final long adjustment = frameTime - mLastFrameTime;
+ final int numAnims = mAnimations.size();
+ for (int i = 0; i < numAnims; ++i) {
+ mAnimations.get(i).commitAnimationFrame(adjustment);
+ }
}
private void scheduleAnimation() {
if (!mAnimationScheduled) {
- mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
+ mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);
mAnimationScheduled = true;
}
}
+
+ // Called by the Choreographer.
+ final Runnable mAnimate = new Runnable() {
+ @Override
+ public void run() {
+ mAnimationScheduled = false;
+ doAnimationFrame(mChoreographer.getFrameTime());
+ }
+ };
+
+ // Called by the Choreographer.
+ final Runnable mCommit = new Runnable() {
+ @Override
+ public void run() {
+ commitAnimationFrame(mChoreographer.getFrameTime());
+ }
+ };
}
/**
@@ -768,6 +817,7 @@
*
* @return the number of milliseconds to delay running the animation
*/
+ @Override
public long getStartDelay() {
return mUnscaledStartDelay;
}
@@ -778,6 +828,7 @@
* @param startDelay The amount of the delay, in milliseconds
*/
+ @Override
public void setStartDelay(long startDelay) {
this.mStartDelay = (long)(startDelay * sDurationScale);
mUnscaledStartDelay = startDelay;
@@ -1148,6 +1199,7 @@
long currentPlayTime = currentTime - mStartTime;
long timeLeft = mDuration - currentPlayTime;
mStartTime = currentTime - timeLeft;
+ mStartTimeCommitted = true; // do not allow start time to be compensated for jank
mReversing = !mReversing;
} else if (mStarted) {
end();
@@ -1254,9 +1306,9 @@
}
long deltaTime = currentTime - mDelayStartTime;
if (deltaTime > mStartDelay) {
- // startDelay ended - start the anim and record the
- // mStartTime appropriately
- mStartTime = currentTime - (deltaTime - mStartDelay);
+ // startDelay ended - start the anim and record the mStartTime appropriately
+ mStartTime = mDelayStartTime + mStartDelay;
+ mStartTimeCommitted = true; // do not allow start time to be compensated for jank
mPlayingState = RUNNING;
return true;
}
@@ -1264,6 +1316,22 @@
}
/**
+ * Applies an adjustment to the animation to compensate for jank between when
+ * the animation first ran and when the frame was drawn.
+ */
+ void commitAnimationFrame(long adjustment) {
+ if (!mStartTimeCommitted) {
+ mStartTimeCommitted = true;
+ if (mPlayingState == RUNNING && adjustment > 0) {
+ mStartTime += adjustment;
+ if (DEBUG) {
+ Log.d(TAG, "Adjusted start time by " + adjustment + " ms: " + toString());
+ }
+ }
+ }
+ }
+
+ /**
* This internal function processes a single animation frame for a given animation. The
* currentTime parameter is the timing pulse sent by the handler, used to calculate the
* elapsed duration, and therefore
@@ -1303,6 +1371,8 @@
mCurrentIteration += (int) fraction;
fraction = fraction % 1f;
mStartTime += mDuration;
+ // Note: We do not need to update the value of mStartTimeCommitted here
+ // since we just added a duration offset.
} else {
done = true;
fraction = Math.min(fraction, 1.0f);
@@ -1334,6 +1404,7 @@
mStartTime = frameTime - seekTime;
mSeekFraction = -1;
}
+ mStartTimeCommitted = false; // allow start time to be compensated for jank
}
if (mPaused) {
if (mPauseTime < 0) {
@@ -1345,6 +1416,7 @@
if (mPauseTime > 0) {
// Offset by the duration that the animation was paused
mStartTime += (frameTime - mPauseTime);
+ mStartTimeCommitted = false; // allow start time to be compensated for jank
}
}
// The frame time might be before the start time during the first frame of
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index d143f8b..8f125d7 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2682,6 +2682,47 @@
}
/**
+ * Request that the system start watching for the calling process to exceed a pss
+ * size as given here. Once called, the system will look for any occassions where it
+ * sees the associated process with a larger pss size and, when this happens, automatically
+ * pull a heap dump from it and allow the user to share the data. Note that this request
+ * continues running even if the process is killed and restarted. To remove the watch,
+ * use {@link #clearWatchHeapLimit()}.
+ *
+ * <p>This API only work if running on a debuggable (userdebug or eng) build.</p>
+ *
+ * <p>Callers can optionally implement {@link #ACTION_REPORT_HEAP_LIMIT} to directly
+ * handle heap limit reports themselves.</p>
+ *
+ * @param pssSize The size in bytes to set the limit at.
+ */
+ public void setWatchHeapLimit(long pssSize) {
+ try {
+ ActivityManagerNative.getDefault().setDumpHeapDebugLimit(null, 0, pssSize,
+ mContext.getPackageName());
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Action an app can implement to handle reports from {@link #setWatchHeapLimit(long)}.
+ * If your package has an activity handling this action, it will be launched with the
+ * heap data provided to it the same way as {@link Intent#ACTION_SEND}. Note that to
+ * match the activty must support this action and a MIME type of "*/*".
+ */
+ public static final String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT";
+
+ /**
+ * Clear a heap watch limit previously set by {@link #setWatchHeapLimit(long)}.
+ */
+ public void clearWatchHeapLimit() {
+ try {
+ ActivityManagerNative.getDefault().setDumpHeapDebugLimit(null, 0, 0, null);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* @hide
*/
public void startLockTaskMode(int taskId) {
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 1484af8..f63d13c 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2427,8 +2427,10 @@
case SET_DUMP_HEAP_DEBUG_LIMIT_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
String procName = data.readString();
+ int uid = data.readInt();
long maxMemSize = data.readLong();
- setDumpHeapDebugLimit(procName, maxMemSize);
+ String reportPackage = data.readString();
+ setDumpHeapDebugLimit(procName, uid, maxMemSize, reportPackage);
reply.writeNoException();
return true;
}
@@ -2450,6 +2452,15 @@
reply.writeNoException();
return true;
}
+
+ case UPDATE_LOCK_TASK_PACKAGES_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int userId = data.readInt();
+ String[] packages = data.readStringArray();
+ updateLockTaskPackages(userId, packages);
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -5644,12 +5655,15 @@
}
@Override
- public void setDumpHeapDebugLimit(String processName, long maxMemSize) throws RemoteException {
+ public void setDumpHeapDebugLimit(String processName, int uid, long maxMemSize,
+ String reportPackage) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeString(processName);
+ data.writeInt(uid);
data.writeLong(maxMemSize);
+ data.writeString(reportPackage);
mRemote.transact(SET_DUMP_HEAP_DEBUG_LIMIT_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
@@ -5682,5 +5696,18 @@
reply.recycle();
}
+ @Override
+ public void updateLockTaskPackages(int userId, String[] packages) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(userId);
+ data.writeStringArray(packages);
+ mRemote.transact(UPDATE_LOCK_TASK_PACKAGES_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 4bd2332..381c20c 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -208,8 +208,12 @@
public static final int OP_ACTIVATE_VPN = 47;
/** @hide Access the WallpaperManagerAPI to write wallpapers. */
public static final int OP_WRITE_WALLPAPER = 48;
+ /** @hide Received the assist structure from an app. */
+ public static final int OP_ASSIST_STRUCTURE = 49;
+ /** @hide Received a screenshot from assist. */
+ public static final int OP_ASSIST_SCREENSHOT = 50;
/** @hide */
- public static final int _NUM_OP = 49;
+ public static final int _NUM_OP = 51;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION =
@@ -288,6 +292,8 @@
OP_PROJECT_MEDIA,
OP_ACTIVATE_VPN,
OP_WRITE_WALLPAPER,
+ OP_ASSIST_STRUCTURE,
+ OP_ASSIST_SCREENSHOT,
};
/**
@@ -344,6 +350,8 @@
null,
OPSTR_ACTIVATE_VPN,
null,
+ null,
+ null,
};
/**
@@ -400,6 +408,8 @@
"PROJECT_MEDIA",
"ACTIVATE_VPN",
"WRITE_WALLPAPER",
+ "ASSIST_STRUCTURE",
+ "ASSIST_SCREENSHOT"
};
/**
@@ -456,6 +466,8 @@
null, // no permission for projecting media
null, // no permission for activating vpn
null, // no permission for supporting wallpaper
+ null, // no permission for receiving assist structure
+ null, // no permission for receiving assist screenshot
};
/**
@@ -513,6 +525,8 @@
null, //PROJECT_MEDIA
UserManager.DISALLOW_CONFIG_VPN, // ACTIVATE_VPN
UserManager.DISALLOW_WALLPAPER, // WRITE_WALLPAPER
+ null, // ASSIST_STRUCTURE
+ null, // ASSIST_SCREENSHOT
};
/**
@@ -569,6 +583,8 @@
false, //PROJECT_MEDIA
false, //ACTIVATE_VPN
false, //WALLPAPER
+ false, //ASSIST_STRUCTURE
+ false, //ASSIST_SCREENSHOT
};
/**
@@ -624,6 +640,8 @@
AppOpsManager.MODE_IGNORED, // OP_PROJECT_MEDIA
AppOpsManager.MODE_IGNORED, // OP_ACTIVATE_VPN
AppOpsManager.MODE_ALLOWED,
+ AppOpsManager.MODE_ALLOWED,
+ AppOpsManager.MODE_ALLOWED,
};
/**
@@ -683,6 +701,8 @@
false,
false,
false,
+ false,
+ false,
};
private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>();
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 6ec5457..10dcd85 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -62,19 +62,19 @@
import android.net.Uri;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArrayMap;
import android.util.Log;
import android.view.Display;
-import android.os.SystemProperties;
+
+import dalvik.system.VMRuntime;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import com.android.internal.util.UserIcons;
-import dalvik.system.VMRuntime;
-
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -1363,7 +1363,17 @@
try {
mPM.movePackage(packageName, observer, flags);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void movePackageAndData(String packageName, String volumeUuid,
+ IPackageMoveObserver observer) {
+ try {
+ mPM.movePackageAndData(packageName, volumeUuid, observer);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
}
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index d794aa3..4a1d6ff 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -482,11 +482,13 @@
public void systemBackupRestored() throws RemoteException;
public void notifyCleartextNetwork(int uid, byte[] firstPacket) throws RemoteException;
- public void setDumpHeapDebugLimit(String processName, long maxMemSize) throws RemoteException;
+ public void setDumpHeapDebugLimit(String processName, int uid, long maxMemSize,
+ String reportPackage) throws RemoteException;
public void dumpHeapFinished(String path) throws RemoteException;
public void setVoiceKeepAwake(IVoiceInteractionSession session, boolean keepAwake)
throws RemoteException;
+ public void updateLockTaskPackages(int userId, String[] packages) throws RemoteException;
/*
* Private non-Binder interfaces
@@ -822,4 +824,5 @@
int SET_DUMP_HEAP_DEBUG_LIMIT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+287;
int DUMP_HEAP_FINISHED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+288;
int SET_VOICE_KEEP_AWAKE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+289;
+ int UPDATE_LOCK_TASK_PACKAGES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+290;
}
diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java
index da7bb05..7acf5f0 100644
--- a/core/java/android/app/VoiceInteractor.java
+++ b/core/java/android/app/VoiceInteractor.java
@@ -103,9 +103,9 @@
request = pullRequest((IVoiceInteractorRequest)args.arg1, true);
if (DEBUG) Log.d(TAG, "onCompleteVoice: req="
+ ((IVoiceInteractorRequest)args.arg1).asBinder() + "/" + request
- + " result=" + args.arg1);
+ + " result=" + args.arg2);
if (request != null) {
- ((CompleteVoiceRequest)request).onCompleteResult((Bundle) args.arg1);
+ ((CompleteVoiceRequest)request).onCompleteResult((Bundle) args.arg2);
request.clear();
}
break;
@@ -297,6 +297,7 @@
*/
public static final class Option implements Parcelable {
final CharSequence mLabel;
+ final int mIndex;
ArrayList<CharSequence> mSynonyms;
Bundle mExtras;
@@ -308,6 +309,21 @@
*/
public Option(CharSequence label) {
mLabel = label;
+ mIndex = -1;
+ }
+
+ /**
+ * Creates an option that a user can select with their voice by matching the label
+ * or one of several synonyms.
+ * @param label The label that will both be matched against what the user speaks
+ * and displayed visually.
+ * @param index The location of this option within the overall set of options.
+ * Can be used to help identify which the option when it is returned from the
+ * voice interactor.
+ */
+ public Option(CharSequence label, int index) {
+ mLabel = label;
+ mIndex = index;
}
/**
@@ -328,6 +344,14 @@
return mLabel;
}
+ /**
+ * Return the index that was supplied in the constructor.
+ * If the option was constructed without an index, -1 is returned.
+ */
+ public int getIndex() {
+ return mIndex;
+ }
+
public int countSynonyms() {
return mSynonyms != null ? mSynonyms.size() : 0;
}
@@ -356,6 +380,7 @@
Option(Parcel in) {
mLabel = in.readCharSequence();
+ mIndex = in.readInt();
mSynonyms = in.readCharSequenceList();
mExtras = in.readBundle();
}
@@ -368,6 +393,7 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeCharSequence(mLabel);
+ dest.writeInt(mIndex);
dest.writeCharSequenceList(mSynonyms);
dest.writeBundle(mExtras);
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index e917522..68f4707 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -502,6 +502,20 @@
*/
public static final String EXTRA_PROVISIONING_BT_USE_PROXY
= "android.app.extra.PROVISIONING_BT_USE_PROXY";
+
+ /**
+ * A {@link android.os.Parcelable} extra of type {@link android.os.PersistableBundle} that
+ * holds data needed by the system to wipe factory reset protection. The data needed to wipe
+ * the device depend on the installed factory reset protection implementation. For example,
+ * if an account is needed to unlock a device, this extra may contain data used to
+ * authenticate that account.
+ *
+ * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
+ * provisioning via an NFC bump.
+ */
+ public static final String EXTRA_PROVISIONING_RESET_PROTECTION_PARAMETERS
+ = "android.app.extra.PROVISIONING_RESET_PROTECTION_PARAMETERS";
+
/**
* This MIME type is used for starting the Device Owner provisioning that does not require
* provisioning features introduced in Android API level
diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java
index ca6dc69..1131ff9 100644
--- a/core/java/android/app/backup/BackupTransport.java
+++ b/core/java/android/app/backup/BackupTransport.java
@@ -464,7 +464,7 @@
* transport level).
*
* <p>After this method returns zero, the system will then call
- * {@link #getNextFullRestorePackage()} to begin the restore process for the next
+ * {@link #nextRestorePackage()} to begin the restore process for the next
* application, and the sequence begins again.
*
* <p>The transport should always close this socket when returning from this method.
diff --git a/core/java/android/app/backup/RecentsBackupHelper.java b/core/java/android/app/backup/RecentsBackupHelper.java
index fd69d20..1a64da6 100644
--- a/core/java/android/app/backup/RecentsBackupHelper.java
+++ b/core/java/android/app/backup/RecentsBackupHelper.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package android.app.backup;
import android.content.Context;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 514802e9..eea47b7 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2908,6 +2908,24 @@
/** {@hide} */
public static final String EXTRA_SETTING_NEW_VALUE = "new_value";
+ /**
+ * Activity Action: Process a piece of text.
+ * <p>Input: {@link #EXTRA_PROCESS_TEXT} contains the text to be processed.
+ * {@link #EXTRA_PROCESS_TEXT_READONLY} states if the resulting text will be read-only.</p>
+ * <p>Output: {@link #EXTRA_PROCESS_TEXT} contains the processed text.</p>
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_PROCESS_TEXT = "android.intent.action.PROCESS_TEXT";
+ /**
+ * The name of the extra used to define the text to be processed.
+ */
+ public static final String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT";
+ /**
+ * The name of the extra used to define if the processed text will be used as read-only.
+ */
+ public static final String EXTRA_PROCESS_TEXT_READONLY =
+ "android.intent.extra.PROCESS_TEXT_READONLY";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard intent categories (see addCategory()).
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 8567c01..5bdb7bb 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -459,6 +459,8 @@
public int largestWidthLimitDp = 0;
/** {@hide} */
+ public String volumeUuid;
+ /** {@hide} */
public String scanSourceDir;
/** {@hide} */
public String scanPublicSourceDir;
@@ -726,6 +728,7 @@
requiresSmallestWidthDp = orig.requiresSmallestWidthDp;
compatibleWidthLimitDp = orig.compatibleWidthLimitDp;
largestWidthLimitDp = orig.largestWidthLimitDp;
+ volumeUuid = orig.volumeUuid;
scanSourceDir = orig.scanSourceDir;
scanPublicSourceDir = orig.scanPublicSourceDir;
sourceDir = orig.sourceDir;
@@ -778,6 +781,7 @@
dest.writeInt(requiresSmallestWidthDp);
dest.writeInt(compatibleWidthLimitDp);
dest.writeInt(largestWidthLimitDp);
+ dest.writeString(volumeUuid);
dest.writeString(scanSourceDir);
dest.writeString(scanPublicSourceDir);
dest.writeString(sourceDir);
@@ -829,6 +833,7 @@
requiresSmallestWidthDp = source.readInt();
compatibleWidthLimitDp = source.readInt();
largestWidthLimitDp = source.readInt();
+ volumeUuid = source.readString();
scanSourceDir = source.readString();
scanPublicSourceDir = source.readString();
sourceDir = source.readString();
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index ba62cd6..154ff85 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -44,7 +44,8 @@
void registerCallback(IPackageInstallerCallback callback, int userId);
void unregisterCallback(IPackageInstallerCallback callback);
- void uninstall(String packageName, int flags, in IntentSender statusReceiver, int userId);
+ void uninstall(String packageName, String callerPackageName, int flags,
+ in IntentSender statusReceiver, int userId);
void setPermissionsResult(int sessionId, boolean accepted);
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index fb7c96d..eed0df5 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -269,6 +269,12 @@
void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage);
/**
+ * Backup/restore support - only the system uid may use these.
+ */
+ byte[] getPreferredActivityBackup(int userId);
+ void restorePreferredActivities(in byte[] backup, int userId);
+
+ /**
* Report the set of 'Home' activity candidates, plus (if any) which of them
* is the current "always use this one" setting.
*/
@@ -426,7 +432,8 @@
PackageCleanItem nextPackageToClean(in PackageCleanItem lastPackage);
void movePackage(String packageName, IPackageMoveObserver observer, int flags);
-
+ void movePackageAndData(String packageName, String volumeUuid, IPackageMoveObserver observer);
+
boolean addPermissionAsync(in PermissionInfo info);
boolean setInstallLocation(int loc);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 80efd0b..b7ee82d 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -423,7 +423,7 @@
*/
public void uninstall(@NonNull String packageName, @NonNull IntentSender statusReceiver) {
try {
- mInstaller.uninstall(packageName, 0, statusReceiver, mUserId);
+ mInstaller.uninstall(packageName, mInstallerPackageName, 0, statusReceiver, mUserId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -887,6 +887,8 @@
public Uri referrerUri;
/** {@hide} */
public String abiOverride;
+ /** {@hide} */
+ public String volumeUuid;
/**
* Construct parameters for a new package install session.
@@ -911,6 +913,7 @@
originatingUri = source.readParcelable(null);
referrerUri = source.readParcelable(null);
abiOverride = source.readString();
+ volumeUuid = source.readString();
}
/**
@@ -1008,6 +1011,7 @@
pw.printPair("originatingUri", originatingUri);
pw.printPair("referrerUri", referrerUri);
pw.printPair("abiOverride", abiOverride);
+ pw.printPair("volumeUuid", volumeUuid);
pw.println();
}
@@ -1028,6 +1032,7 @@
dest.writeParcelable(originatingUri, flags);
dest.writeParcelable(referrerUri, flags);
dest.writeString(abiOverride);
+ dest.writeString(volumeUuid);
}
public static final Parcelable.Creator<SessionParams>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 46d6ffb3..4c99d09 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -43,7 +43,9 @@
import android.os.Environment;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.storage.VolumeInfo;
import android.util.AndroidException;
+
import com.android.internal.util.ArrayUtils;
import java.io.File;
@@ -339,15 +341,17 @@
public static final int INSTALL_ALLOW_TEST = 0x00000004;
/**
- * Flag parameter for {@link #installPackage} to indicate that this
- * package has to be installed on the sdcard.
+ * Flag parameter for {@link #installPackage} to indicate that this package
+ * must be installed to an ASEC on a {@link VolumeInfo#TYPE_PUBLIC}.
+ *
* @hide
*/
public static final int INSTALL_EXTERNAL = 0x00000008;
/**
* Flag parameter for {@link #installPackage} to indicate that this package
- * has to be installed on the sdcard.
+ * must be installed to internal storage.
+ *
* @hide
*/
public static final int INSTALL_INTERNAL = 0x00000010;
@@ -4099,8 +4103,12 @@
*
* @hide
*/
- public abstract void movePackage(
- String packageName, IPackageMoveObserver observer, int flags);
+ @Deprecated
+ public abstract void movePackage(String packageName, IPackageMoveObserver observer, int flags);
+
+ /** {@hide} */
+ public abstract void movePackageAndData(String packageName, String volumeUuid,
+ IPackageMoveObserver observer);
/**
* Returns the device identity that verifiers can use to associate their scheme to a particular
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index f4ad434..c1e6a4d 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -604,7 +604,7 @@
public final static int PARSE_MUST_BE_APK = 1<<2;
public final static int PARSE_IGNORE_PROCESSES = 1<<3;
public final static int PARSE_FORWARD_LOCK = 1<<4;
- public final static int PARSE_ON_SDCARD = 1<<5;
+ public final static int PARSE_EXTERNAL_STORAGE = 1<<5;
public final static int PARSE_IS_SYSTEM_DIR = 1<<6;
public final static int PARSE_IS_PRIVILEGED = 1<<7;
public final static int PARSE_COLLECT_CERTIFICATES = 1<<8;
@@ -1408,7 +1408,7 @@
}
/* Set the global "on SD card" flag */
- if ((flags & PARSE_ON_SDCARD) != 0) {
+ if ((flags & PARSE_EXTERNAL_STORAGE) != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
}
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index b5eeb30..bc6d4ce 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1284,14 +1284,12 @@
/**
* Set the locale. This is the preferred way for setting up the locale (instead of using the
- * direct accessor). This will also set the userLocale and layout direction according to
- * the locale.
+ * direct accessor). This will also set the layout direction according to the locale.
*
* @param loc The locale. Can be null.
*/
public void setLocale(Locale loc) {
locale = loc;
- userSetLocale = true;
setLayoutDirection(locale);
}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 299eb7e..334d180 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -117,6 +117,9 @@
private static final LongSparseArray<android.content.res.ConstantState<ColorStateList>>
sPreloadedColorStateLists = new LongSparseArray<>();
+ private static final String CACHE_NOT_THEMED = "";
+ private static final String CACHE_NULL_THEME = "null_theme";
+
// Pool of TypedArrays targeted to this Resources object.
final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<>(5);
@@ -2441,8 +2444,8 @@
}
}
- // Next, check preloaded drawables. These are unthemed but may have
- // themeable attributes.
+ // Next, check preloaded drawables. These may contain unresolved theme
+ // attributes.
final ConstantState cs;
if (isColorDrawable) {
cs = sPreloadedColorDrawables.get(key);
@@ -2450,42 +2453,49 @@
cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
}
- final Drawable dr;
+ Drawable dr;
if (cs != null) {
- final Drawable clonedDr = cs.newDrawable(this);
- if (theme != null) {
- dr = clonedDr.mutate();
- dr.applyTheme(theme);
- dr.clearMutated();
- } else {
- dr = clonedDr;
- }
+ dr = cs.newDrawable(this);
} else if (isColorDrawable) {
dr = new ColorDrawable(value.data);
} else {
- dr = loadDrawableForCookie(value, id, theme);
+ dr = loadDrawableForCookie(value, id, null);
+ }
+
+ // Determine if the drawable has unresolved theme attributes. If it
+ // does, we'll need to apply a theme and store it in a theme-specific
+ // cache.
+ final String cacheKey;
+ if (!dr.canApplyTheme()) {
+ cacheKey = CACHE_NOT_THEMED;
+ } else if (theme == null) {
+ cacheKey = CACHE_NULL_THEME;
+ } else {
+ cacheKey = theme.getKey();
+ dr = dr.mutate();
+ dr.applyTheme(theme);
+ dr.clearMutated();
}
// If we were able to obtain a drawable, store it in the appropriate
- // cache (either preload or themed).
+ // cache: preload, not themed, null theme, or theme-specific.
if (dr != null) {
dr.setChangingConfigurations(value.changingConfigurations);
- cacheDrawable(value, theme, isColorDrawable, caches, key, dr);
+ cacheDrawable(value, isColorDrawable, caches, cacheKey, key, dr);
}
return dr;
}
- private void cacheDrawable(TypedValue value, Theme theme, boolean isColorDrawable,
+ private void cacheDrawable(TypedValue value, boolean isColorDrawable,
ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
- long key, Drawable dr) {
+ String cacheKey, long key, Drawable dr) {
final ConstantState cs = dr.getConstantState();
if (cs == null) {
return;
}
if (mPreloading) {
- // Preloaded drawables never have a theme, but may be themeable.
final int changingConfigs = cs.getChangingConfigurations();
if (isColorDrawable) {
if (verifyPreloadConfig(changingConfigs, 0, value.resourceId, "drawable")) {
@@ -2507,14 +2517,13 @@
}
} else {
synchronized (mAccessLock) {
- final String themeKey = theme == null ? "" : theme.mKey;
- LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(themeKey);
+ LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(cacheKey);
if (themedCache == null) {
// Clean out the caches before we add more. This shouldn't
// happen very often.
pruneCaches(caches);
themedCache = new LongSparseArray<>(1);
- caches.put(themeKey, themedCache);
+ caches.put(cacheKey, themedCache);
}
themedCache.put(key, new WeakReference<>(cs));
}
@@ -2612,42 +2621,43 @@
ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
long key, Theme theme) {
synchronized (mAccessLock) {
- final String themeKey = theme != null ? theme.mKey : "";
- final LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(themeKey);
- if (themedCache != null) {
- final Drawable themedDrawable = getCachedDrawableLocked(themedCache, key);
- if (themedDrawable != null) {
- return themedDrawable;
- }
+ // First search theme-agnostic cache.
+ final Drawable unthemedDrawable = getCachedDrawableLocked(
+ caches, key, CACHE_NOT_THEMED);
+ if (unthemedDrawable != null) {
+ return unthemedDrawable;
}
- // No cached drawable, we'll need to create a new one.
- return null;
+ // Next search theme-specific cache.
+ final String themeKey = theme != null ? theme.getKey() : CACHE_NULL_THEME;
+ return getCachedDrawableLocked(caches, key, themeKey);
}
}
- private ConstantState getConstantStateLocked(
- LongSparseArray<WeakReference<ConstantState>> drawableCache, long key) {
- final WeakReference<ConstantState> wr = drawableCache.get(key);
- if (wr != null) { // we have the key
- final ConstantState entry = wr.get();
+ private Drawable getCachedDrawableLocked(
+ ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
+ long key, String themeKey) {
+ final LongSparseArray<WeakReference<ConstantState>> cache = caches.get(themeKey);
+ if (cache != null) {
+ final ConstantState entry = getConstantStateLocked(cache, key);
if (entry != null) {
- //Log.i(TAG, "Returning cached drawable @ #" +
- // Integer.toHexString(((Integer)key).intValue())
- // + " in " + this + ": " + entry);
- return entry;
- } else { // our entry has been purged
- drawableCache.delete(key);
+ return entry.newDrawable(this);
}
}
return null;
}
- private Drawable getCachedDrawableLocked(
+ private ConstantState getConstantStateLocked(
LongSparseArray<WeakReference<ConstantState>> drawableCache, long key) {
- final ConstantState entry = getConstantStateLocked(drawableCache, key);
- if (entry != null) {
- return entry.newDrawable(this);
+ final WeakReference<ConstantState> wr = drawableCache.get(key);
+ if (wr != null) {
+ final ConstantState entry = wr.get();
+ if (entry != null) {
+ return entry;
+ } else {
+ // Our entry has been purged.
+ drawableCache.delete(key);
+ }
}
return null;
}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 1503bf5..f28c96e 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2354,8 +2354,8 @@
* <p>Camera devices that support the MANUAL_POST_PROCESSING capability will always contain
* at least one of below mode combinations:</p>
* <ul>
- * <li>CONTRAST_CURVE and FAST</li>
- * <li>GAMMA_VALUE, PRESET_CURVE, and FAST</li>
+ * <li>CONTRAST_CURVE, FAST and HIGH_QUALITY</li>
+ * <li>GAMMA_VALUE, PRESET_CURVE, FAST and HIGH_QUALITY</li>
* </ul>
* <p>This includes all FULL level devices.</p>
* <p><b>Range of valid values:</b><br>
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 7f901c8..85c574a 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -852,8 +852,8 @@
/**
* <p>Color correction processing operates at improved
- * quality but reduced capture rate (relative to sensor raw
- * output).</p>
+ * quality but the capture rate might be reduced (relative to sensor
+ * raw output rate)</p>
* <p>Advanced white balance adjustments above and beyond
* the specified white balance pipeline may be applied.</p>
* <p>If AWB is enabled with <code>{@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} != OFF</code>, then
@@ -883,8 +883,8 @@
public static final int COLOR_CORRECTION_ABERRATION_MODE_FAST = 1;
/**
- * <p>Aberration correction operates at improved quality but reduced
- * capture rate (relative to sensor raw output).</p>
+ * <p>Aberration correction operates at improved quality but the capture rate might be
+ * reduced (relative to sensor raw output rate)</p>
* @see CaptureRequest#COLOR_CORRECTION_ABERRATION_MODE
*/
public static final int COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY = 2;
@@ -1797,7 +1797,7 @@
public static final int EDGE_MODE_FAST = 1;
/**
- * <p>Apply high-quality edge enhancement, at a cost of reducing output frame rate.</p>
+ * <p>Apply high-quality edge enhancement, at a cost of possibly reduced output frame rate.</p>
* @see CaptureRequest#EDGE_MODE
*/
public static final int EDGE_MODE_HIGH_QUALITY = 2;
@@ -1852,7 +1852,7 @@
/**
* <p>High-quality hot pixel correction is applied, at a cost
- * of reducing frame rate relative to sensor raw output.</p>
+ * of possibly reduced frame rate relative to sensor raw output.</p>
* <p>The hotpixel map may be returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}.</p>
*
* @see CaptureResult#STATISTICS_HOT_PIXEL_MAP
@@ -1894,8 +1894,8 @@
public static final int NOISE_REDUCTION_MODE_FAST = 1;
/**
- * <p>High-quality noise reduction is applied, at the cost of reducing frame rate
- * relative to sensor output.</p>
+ * <p>High-quality noise reduction is applied, at the cost of possibly reduced frame
+ * rate relative to sensor output.</p>
* @see CaptureRequest#NOISE_REDUCTION_MODE
*/
public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2;
@@ -2032,7 +2032,7 @@
/**
* <p>Apply high-quality lens shading correction, at the
- * cost of reduced frame rate.</p>
+ * cost of possibly reduced frame rate.</p>
* @see CaptureRequest#SHADING_MODE
*/
public static final int SHADING_MODE_HIGH_QUALITY = 2;
@@ -2105,7 +2105,7 @@
/**
* <p>High-quality gamma mapping and color enhancement will be applied, at
- * the cost of reduced frame rate compared to raw sensor output.</p>
+ * the cost of possibly reduced frame rate compared to raw sensor output.</p>
* @see CaptureRequest#TONEMAP_MODE
*/
public static final int TONEMAP_MODE_HIGH_QUALITY = 2;
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 486f9b4..5a5d8b7 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -43,6 +43,7 @@
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -231,8 +232,7 @@
mLooper = looper;
mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
if (mMountService == null) {
- Log.e(TAG, "Unable to connect to mount service! - is it running yet?");
- return;
+ throw new IllegalStateException("Failed to find running mount service");
}
}
@@ -442,6 +442,42 @@
}
/** {@hide} */
+ public @Nullable DiskInfo findDiskById(String id) {
+ Preconditions.checkNotNull(id);
+ // TODO; go directly to service to make this faster
+ for (DiskInfo disk : getDisks()) {
+ if (Objects.equals(disk.id, id)) {
+ return disk;
+ }
+ }
+ return null;
+ }
+
+ /** {@hide} */
+ public @Nullable VolumeInfo findVolumeById(String id) {
+ Preconditions.checkNotNull(id);
+ // TODO; go directly to service to make this faster
+ for (VolumeInfo vol : getVolumes()) {
+ if (Objects.equals(vol.id, id)) {
+ return vol;
+ }
+ }
+ return null;
+ }
+
+ /** {@hide} */
+ public @Nullable VolumeInfo findVolumeByUuid(String fsUuid) {
+ Preconditions.checkNotNull(fsUuid);
+ // TODO; go directly to service to make this faster
+ for (VolumeInfo vol : getVolumes()) {
+ if (Objects.equals(vol.fsUuid, fsUuid)) {
+ return vol;
+ }
+ }
+ return null;
+ }
+
+ /** {@hide} */
public @NonNull List<VolumeInfo> getVolumes() {
try {
return Arrays.asList(mMountService.getVolumes());
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index c8c2f65..2dc0361 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -44,6 +44,7 @@
* @hide
*/
public class VolumeInfo implements Parcelable {
+ /** Real volume representing internal emulated storage */
public static final String ID_EMULATED_INTERNAL = "emulated";
public static final int TYPE_PUBLIC = 0;
diff --git a/core/java/android/provider/AlarmClock.java b/core/java/android/provider/AlarmClock.java
index 724d76d..25a35e1 100644
--- a/core/java/android/provider/AlarmClock.java
+++ b/core/java/android/provider/AlarmClock.java
@@ -43,8 +43,14 @@
* should remove this alarm after it has been dismissed. If an identical alarm exists matching
* all parameters, the implementation may re-use it instead of creating a new one (in this case,
* the alarm should not be removed after dismissal).
- *
+ * </p><p>
* This action always enables the alarm.
+ * </p><p>
+ * This activity could also be started in Voice Interaction mode. The activity should check
+ * {@link android.app.Activity#isVoiceInteraction}, and if true, the implementation should
+ * report a deeplink of the created/enabled alarm using
+ * {@link android.app.VoiceInteractor.CompleteVoiceRequest}. This allows follow-on voice actions
+ * such as {@link #ACTION_VOICE_CANCEL_ALARM} to cancel the alarm that was just enabled.
* </p>
* <h3>Request parameters</h3>
* <ul>
@@ -63,6 +69,48 @@
public static final String ACTION_SET_ALARM = "android.intent.action.SET_ALARM";
/**
+ * Voice Activity Action: Cancel an alarm.
+ * Requires: The activity must check {@link android.app.Activity#isVoiceInteraction}, i.e. be
+ * started in Voice Interaction mode.
+ * <p>
+ * Cancels the specified alarm by voice. To cancel means to disable, but not delete, the alarm.
+ * See {@link #ACTION_VOICE_DELETE_ALARM} to delete an alarm by voice.
+ * </p><p>
+ * The alarm to cancel can be specified or searched for in one of the following ways:
+ * <ol>
+ * <li>The Intent's data URI specifies a deeplink to the alarm.
+ * <li>If the Intent's data URI is unspecified, then the extra {@link #EXTRA_ALARM_SEARCH_MODE} is
+ * required to determine how to search for the alarm.
+ *
+ * @see #ACTION_VOICE_DELETE_ALARM
+ * @see #EXTRA_ALARM_SEARCH_MODE
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_VOICE_CANCEL_ALARM =
+ "android.intent.action.VOICE_CANCEL_ALARM";
+
+ /**
+ * Voice Activity Action: Delete an alarm.
+ * Requires: The activity must check {@link android.app.Activity#isVoiceInteraction}, i.e. be
+ * started in Voice Interaction mode.
+ * <p>
+ * Deletes the specified alarm by voice.
+ * See {@link #ACTION_VOICE_CANCEL_ALARM} to cancel (disable) an alarm by voice.
+ * </p><p>
+ * The alarm to delete can be specified or searched for in one of the following ways:
+ * <ol>
+ * <li>The Intent's data URI specifies a deeplink to the alarm.
+ * <li>If the Intent's data URI is unspecified, then the extra {@link #EXTRA_ALARM_SEARCH_MODE} is
+ * required to determine how to search for the alarm.
+ *
+ * @see #ACTION_VOICE_CANCEL_ALARM
+ * @see #EXTRA_ALARM_SEARCH_MODE
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_VOICE_DELETE_ALARM =
+ "android.intent.action.VOICE_DELETE_ALARM";
+
+ /**
* Activity Action: Set a timer.
* <p>
* Activates an existing timer or creates a new one.
@@ -99,6 +147,100 @@
public static final String ACTION_SHOW_ALARMS = "android.intent.action.SHOW_ALARMS";
/**
+ * Bundle extra: Specify the type of search mode to look up an alarm.
+ * <p>
+ * Used by {@link #ACTION_VOICE_CANCEL_ALARM} and {@link #ACTION_VOICE_DELETE_ALARM} to identify
+ * the alarm(s) to cancel or delete, respectively.
+ * </p><p>
+ * This extra is only required when the alarm is not already identified by a deeplink as
+ * specified in the Intent's data URI.
+ * </p><p>
+ * The value of this extra is a {@link String}, restricted to the following set of supported
+ * search modes:
+ * <ul>
+ * <li><i>Time</i> - {@link #ALARM_SEARCH_MODE_TIME}: Selects the alarm that is most
+ * closely matched by the search parameters {@link #EXTRA_HOUR}, {@link #EXTRA_MINUTES},
+ * {@link #EXTRA_IS_PM}.
+ * <li><i>Next alarm</i> - {@link #ALARM_SEARCH_MODE_NEXT}: Selects the alarm that will
+ * ring next, or the alarm that is currently ringing, if any.
+ * <li><i>All alarms</i> - {@link #ALARM_SEARCH_MODE_ALL}: Selects all alarms.
+ * <li><i>None</i> - {@link #ALARM_SEARCH_MODE_NONE}: No search mode specified. The
+ * implementation should ask the user to select a search mode using
+ * {@link android.app.VoiceInteractor.PickOptionRequest} and proceed with a voice flow to
+ * identify the alarm.
+ * </ul>
+ * </ol>
+ *
+ * @see #ALARM_SEARCH_MODE_TIME
+ * @see #ALARM_SEARCH_MODE_NEXT
+ * @see #ALARM_SEARCH_MODE_ALL
+ * @see #ALARM_SEARCH_MODE_NONE
+ * @see #ACTION_VOICE_CANCEL_ALARM
+ * @see #ACTION_VOICE_DELETE_ALARM
+ */
+ public static final String EXTRA_ALARM_SEARCH_MODE =
+ "android.intent.extra.alarm.ALARM_SEARCH_MODE";
+
+ /**
+ * Search for the alarm that is most closely matched by the search parameters
+ * {@link #EXTRA_HOUR}, {@link #EXTRA_MINUTES}, {@link #EXTRA_IS_PM}.
+ * In this search mode, at least one of these additional extras are required.
+ * <ul>
+ * <li>{@link #EXTRA_HOUR} - The hour to search for the alarm.
+ * <li>{@link #EXTRA_MINUTES} - The minute to search for the alarm.
+ * <li>{@link #EXTRA_IS_PM} - Whether the hour is AM or PM.
+ * </ul>
+ *
+ * @see #EXTRA_ALARM_SEARCH_MODE
+ */
+ public static final String ALARM_SEARCH_MODE_TIME = "time";
+
+ /**
+ * Selects the alarm that will ring next, or the alarm that is currently ringing, if any.
+ *
+ * @see #EXTRA_ALARM_SEARCH_MODE
+ */
+ public static final String ALARM_SEARCH_MODE_NEXT = "next";
+
+ /**
+ * Selects all alarms.
+ *
+ * @see #EXTRA_ALARM_SEARCH_MODE
+ */
+ public static final String ALARM_SEARCH_MODE_ALL = "all";
+
+ /**
+ * No search mode specified. The implementation should ask the user to select a search mode
+ * using {@link android.app.VoiceInteractor.PickOptionRequest} and proceed with a voice flow to
+ * identify the alarm.
+ *
+ * @see #EXTRA_ALARM_SEARCH_MODE
+ */
+ public static final String ALARM_SEARCH_MODE_NONE = "none";
+
+ /**
+ * Bundle extra: The AM/PM of the alarm.
+ * <p>
+ * Used by {@link #ACTION_VOICE_CANCEL_ALARM} and {@link #ACTION_VOICE_DELETE_ALARM}.
+ * </p><p>
+ * This extra is optional and only used when {@link #EXTRA_ALARM_SEARCH_MODE} is set to
+ * {@link #ALARM_SEARCH_MODE_TIME}. In this search mode, the {@link #EXTRA_IS_PM} is
+ * used together with {@link #EXTRA_HOUR} and {@link #EXTRA_MINUTES}. The implementation should
+ * look up the alarm that is most closely matched by these search parameters.
+ * If {@link #EXTRA_IS_PM} is missing, then the AM/PM of the specified {@link #EXTRA_HOUR} is
+ * ambiguous and the implementation should ask for clarification from the user.
+ * </p><p>
+ * The value is a {@link Boolean}, where false=AM and true=PM.
+ * </p>
+ *
+ * @see #ACTION_VOICE_CANCEL_ALARM
+ * @see #ACTION_VOICE_DELETE_ALARM
+ * @see #EXTRA_HOUR
+ * @see #EXTRA_MINUTES
+ */
+ public static final String EXTRA_IS_PM = "android.intent.extra.alarm.IS_PM";
+
+ /**
* Bundle extra: Weekdays for repeating alarm.
* <p>
* Used by {@link #ACTION_SET_ALARM}.
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 5afbd6d..7565654b 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -226,6 +226,35 @@
public static final String INTENT_ACTION_STILL_IMAGE_CAMERA = "android.media.action.STILL_IMAGE_CAMERA";
/**
+ * The name of the Intent action used to indicate that a camera launch might be imminent. This
+ * broadcast should be targeted to the package that is receiving
+ * {@link #INTENT_ACTION_STILL_IMAGE_CAMERA} or
+ * {@link #INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE}, depending on the context. If such
+ * intent would launch the resolver activity, this broadcast should not be sent at all.
+ * <p>
+ * A receiver of this broadcast should do the absolute minimum amount of work to initialize the
+ * camera in order to reduce startup time in likely case that shortly after an actual camera
+ * launch intent would be sent.
+ * <p>
+ * In case the actual intent will not be fired, the target package will receive
+ * {@link #ACTION_STILL_IMAGE_CAMERA_COOLDOWN}. However, it is recommended that the receiver
+ * also implements a timeout to close the camera after receiving this intent, as there is no
+ * guarantee that {@link #ACTION_STILL_IMAGE_CAMERA_COOLDOWN} will be delivered.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_STILL_IMAGE_CAMERA_PREWARM = "android.media.action.STILL_IMAGE_CAMERA_PREWARM";
+
+ /**
+ * The name of the Intent action used to indicate that an imminent camera launch has been
+ * cancelled by the user. This broadcast should be targeted to the package that has received
+ * {@link #ACTION_STILL_IMAGE_CAMERA_PREWARM}.
+ * <p>
+ * A receiver of this broadcast should close the camera immediately.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_STILL_IMAGE_CAMERA_COOLDOWN = "android.media.action.STILL_IMAGE_CAMERA_COOLDOWN";
+
+ /**
* The name of the Intent action used to launch a camera in still image mode
* for use when the device is secured (e.g. with a pin, password, pattern,
* or face unlock). Applications responding to this intent must not expose
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index e94a312..5e2accd 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -171,6 +171,10 @@
public static final int KM_KEY_FORMAT_PKCS12 = 2;
public static final int KM_KEY_FORMAT_RAW = 3;
+ // User authenticators.
+ public static final int HW_AUTH_PASSWORD = 1 << 0;
+ public static final int HW_AUTH_FINGERPRINT = 1 << 1;
+
// Error codes.
public static final int KM_ERROR_OK = 0;
public static final int KM_ERROR_ROOT_OF_TRUST_ALREADY_SET = -1;
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 4c31f80..20d7079 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -520,6 +520,10 @@
mCallbacks, true);
}
+ public Context getContext() {
+ return mContext;
+ }
+
Request newRequest(IVoiceInteractorCallback callback) {
synchronized (this) {
Request req = new Request(callback, this);
@@ -832,6 +836,12 @@
return false;
}
+ /**
+ * Called when the user presses the back button while focus is in the session UI. Note
+ * that this will only happen if the session UI has requested input focus in its window;
+ * otherwise, the back key will go to whatever window has focus and do whatever behavior
+ * it normally has there.
+ */
public void onBackPressed() {
hide();
}
diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java
index f4dff9b..a99bdf5 100644
--- a/core/java/android/text/Hyphenator.java
+++ b/core/java/android/text/Hyphenator.java
@@ -30,7 +30,7 @@
*
* @hide
*/
-/* package */ class Hyphenator {
+public class Hyphenator {
// This class has deliberately simple lifetime management (no finalizer) because in
// the common case a process will use a very small number of locales.
@@ -45,20 +45,14 @@
}
public static long get(Locale locale) {
- synchronized (sMap) {
- Hyphenator result = sMap.get(locale);
- if (result == null) {
- result = loadHyphenator(locale);
- sMap.put(locale, result);
- }
- return result == null ? 0 : result.mNativePtr;
- }
+ Hyphenator result = sMap.get(locale);
+ return result == null ? 0 : result.mNativePtr;
}
private static Hyphenator loadHyphenator(Locale locale) {
// TODO: find pattern dictionary (from system location) that best matches locale
if (Locale.US.equals(locale)) {
- File f = new File("/data/local/tmp/hyph-en-us.pat.txt");
+ File f = new File(getSystemHyphenatorLocation(), "hyph-en-us.pat.txt");
try {
RandomAccessFile rf = new RandomAccessFile(f, "r");
byte[] buf = new byte[(int)rf.length()];
@@ -68,9 +62,26 @@
long nativePtr = StaticLayout.nLoadHyphenator(patternData);
return new Hyphenator(nativePtr);
} catch (IOException e) {
- Log.e(TAG, "error loading hyphenation " + f);
+ Log.e(TAG, "error loading hyphenation " + f, e);
}
}
return null;
}
+
+ private static File getSystemHyphenatorLocation() {
+ // TODO: move to a sensible location under system
+ return new File("/system/usr/hyphen-data");
+ }
+
+ /**
+ * Load hyphenation patterns at initialization time. We want to have patterns
+ * for all locales loaded and ready to use so we don't have to do any file IO
+ * on the UI thread when drawing text in different locales.
+ *
+ * @hide
+ */
+ public static void init() {
+ Locale l = Locale.US;
+ sMap.put(l, loadHyphenator(l));
+ }
}
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index c8149d9..79a8489 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -71,7 +71,12 @@
*/
public final class Choreographer {
private static final String TAG = "Choreographer";
- private static final boolean DEBUG = false;
+
+ // Prints debug messages about jank which was detected (low volume).
+ private static final boolean DEBUG_JANK = false;
+
+ // Prints debug messages about every frame and callback registered (high volume).
+ private static final boolean DEBUG_FRAMES = false;
// The default amount of time in ms between animation frames.
// When vsync is not enabled, we want to have some idea of how long we should
@@ -139,6 +144,7 @@
private boolean mCallbacksRunning;
private long mLastFrameTimeNanos;
private long mFrameIntervalNanos;
+ private boolean mDebugPrintNextFrameTimeDelta;
/**
* Contains information about the current frame for jank-tracking,
@@ -166,13 +172,25 @@
public static final int CALLBACK_ANIMATION = 1;
/**
- * Callback type: Traversal callback. Handles layout and draw. Runs last
+ * Callback type: Traversal callback. Handles layout and draw. Runs
* after all other asynchronous messages have been handled.
* @hide
*/
public static final int CALLBACK_TRAVERSAL = 2;
- private static final int CALLBACK_LAST = CALLBACK_TRAVERSAL;
+ /**
+ * Callback type: Commit callback. Handles post-draw operations for the frame.
+ * Runs after traversal completes. The {@link #getFrameTime() frame time} reported
+ * during this callback may be updated to reflect delays that occurred while
+ * traversals were in progress in case heavy layout operations caused some frames
+ * to be skipped. The frame time reported during this callback provides a better
+ * estimate of the start time of the frame in which animations (and other updates
+ * to the view hierarchy state) actually took effect.
+ * @hide
+ */
+ public static final int CALLBACK_COMMIT = 3;
+
+ private static final int CALLBACK_LAST = CALLBACK_COMMIT;
private Choreographer(Looper looper) {
mLooper = looper;
@@ -332,7 +350,7 @@
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
- if (DEBUG) {
+ if (DEBUG_FRAMES) {
Log.d(TAG, "PostCallback: type=" + callbackType
+ ", action=" + action + ", token=" + token
+ ", delayMillis=" + delayMillis);
@@ -376,7 +394,7 @@
}
private void removeCallbacksInternal(int callbackType, Object action, Object token) {
- if (DEBUG) {
+ if (DEBUG_FRAMES) {
Log.d(TAG, "RemoveCallbacks: type=" + callbackType
+ ", action=" + action + ", token=" + token);
}
@@ -492,7 +510,7 @@
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {
- if (DEBUG) {
+ if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame on vsync.");
}
@@ -509,7 +527,7 @@
} else {
final long nextFrameTime = Math.max(
mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
- if (DEBUG) {
+ if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
}
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
@@ -526,6 +544,12 @@
return; // no work to do
}
+ if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) {
+ mDebugPrintNextFrameTimeDelta = false;
+ Log.d(TAG, "Frame time delta: "
+ + ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms");
+ }
+
long intendedFrameTimeNanos = frameTimeNanos;
startNanos = System.nanoTime();
final long jitterNanos = startNanos - frameTimeNanos;
@@ -536,7 +560,7 @@
+ "The application may be doing too much work on its main thread.");
}
final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
- if (DEBUG) {
+ if (DEBUG_JANK) {
Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
+ "which is more than the frame interval of "
+ (mFrameIntervalNanos * 0.000001f) + " ms! "
@@ -547,7 +571,7 @@
}
if (frameTimeNanos < mLastFrameTimeNanos) {
- if (DEBUG) {
+ if (DEBUG_JANK) {
Log.d(TAG, "Frame time appears to be going backwards. May be due to a "
+ "previously skipped frame. Waiting for next vsync.");
}
@@ -569,7 +593,9 @@
mFrameInfo.markPerformTraversalsStart();
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
- if (DEBUG) {
+ doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
+
+ if (DEBUG_FRAMES) {
final long endNanos = System.nanoTime();
Log.d(TAG, "Frame " + frame + ": Finished, took "
+ (endNanos - startNanos) * 0.000001f + " ms, latency "
@@ -583,16 +609,43 @@
// We use "now" to determine when callbacks become due because it's possible
// for earlier processing phases in a frame to post callbacks that should run
// in a following phase, such as an input event that causes an animation to start.
- final long now = SystemClock.uptimeMillis();
- callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now);
+ final long now = System.nanoTime();
+ callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
+ now / TimeUtils.NANOS_PER_MS);
if (callbacks == null) {
return;
}
mCallbacksRunning = true;
+
+ // Update the frame time if necessary when committing the frame.
+ // We only update the frame time if we are more than 2 frames late reaching
+ // the commit phase. This ensures that the frame time which is observed by the
+ // callbacks will always increase from one frame to the next and never repeat.
+ // We never want the next frame's starting frame time to end up being less than
+ // or equal to the previous frame's commit frame time. Keep in mind that the
+ // next frame has most likely already been scheduled by now so we play it
+ // safe by ensuring the commit time is always at least one frame behind.
+ if (callbackType == Choreographer.CALLBACK_COMMIT) {
+ final long jitterNanos = now - frameTimeNanos;
+ if (jitterNanos >= 2 * mFrameIntervalNanos) {
+ final long lastFrameOffset = jitterNanos % mFrameIntervalNanos
+ + mFrameIntervalNanos;
+ if (DEBUG_JANK) {
+ Log.d(TAG, "Commit callback delayed by " + (jitterNanos * 0.000001f)
+ + " ms which is more than twice the frame interval of "
+ + (mFrameIntervalNanos * 0.000001f) + " ms! "
+ + "Setting frame time to " + (lastFrameOffset * 0.000001f)
+ + " ms in the past.");
+ mDebugPrintNextFrameTimeDelta = true;
+ }
+ frameTimeNanos = now - lastFrameOffset;
+ mLastFrameTimeNanos = frameTimeNanos;
+ }
+ }
}
try {
for (CallbackRecord c = callbacks; c != null; c = c.next) {
- if (DEBUG) {
+ if (DEBUG_FRAMES) {
Log.d(TAG, "RunCallback: type=" + callbackType
+ ", action=" + c.action + ", token=" + c.token
+ ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 384bd2c..b6f1e3b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8400,7 +8400,7 @@
return true;
}
} break;
- case R.id.accessibility_action_show_on_screen: {
+ case R.id.accessibilityActionShowOnScreen: {
if (mAttachInfo != null) {
final Rect r = mAttachInfo.mTmpInvalRect;
getDrawingRect(r);
@@ -16918,7 +16918,7 @@
@Nullable
public ColorStateList getForegroundTintList() {
return mForegroundInfo != null && mForegroundInfo.mTintInfo != null
- ? mBackgroundTint.mTintList : null;
+ ? mForegroundInfo.mTintInfo.mTintList : null;
}
/**
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index ec527d5..0736ed8 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -266,7 +266,8 @@
* Action to set the selection. Performing this action with no arguments
* clears the selection.
* <p>
- * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_SELECTION_START_INT},
+ * <strong>Arguments:</strong>
+ * {@link #ACTION_ARGUMENT_SELECTION_START_INT},
* {@link #ACTION_ARGUMENT_SELECTION_END_INT}<br>
* <strong>Example:</strong>
* <code><pre><p>
@@ -302,7 +303,8 @@
* null</code> or empty {@link CharSequence} will clear the text. This action will also put the
* cursor at the end of text.
* <p>
- * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}<br>
+ * <strong>Arguments:</strong>
+ * {@link #ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}<br>
* <strong>Example:</strong>
* <code><pre><p>
* Bundle arguments = new Bundle();
@@ -326,12 +328,15 @@
* Argument for which movement granularity to be used when traversing the node text.
* <p>
* <strong>Type:</strong> int<br>
- * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
- * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
+ * <strong>Actions:</strong>
+ * <ul>
+ * <li>{@link AccessibilityAction#ACTION_NEXT_AT_MOVEMENT_GRANULARITY}</li>
+ * <li>{@link AccessibilityAction#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}</li>
+ * </ul>
* </p>
*
- * @see #ACTION_NEXT_AT_MOVEMENT_GRANULARITY
- * @see #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ * @see AccessibilityAction#ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ * @see AccessibilityAction#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
*/
public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT =
"ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
@@ -340,12 +345,15 @@
* Argument for which HTML element to get moving to the next/previous HTML element.
* <p>
* <strong>Type:</strong> String<br>
- * <strong>Actions:</strong> {@link #ACTION_NEXT_HTML_ELEMENT},
- * {@link #ACTION_PREVIOUS_HTML_ELEMENT}
+ * <strong>Actions:</strong>
+ * <ul>
+ * <li>{@link AccessibilityAction#ACTION_NEXT_HTML_ELEMENT}</li>
+ * <li>{@link AccessibilityAction#ACTION_PREVIOUS_HTML_ELEMENT}</li>
+ * </ul>
* </p>
*
- * @see #ACTION_NEXT_HTML_ELEMENT
- * @see #ACTION_PREVIOUS_HTML_ELEMENT
+ * @see AccessibilityAction#ACTION_NEXT_HTML_ELEMENT
+ * @see AccessibilityAction#ACTION_PREVIOUS_HTML_ELEMENT
*/
public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING =
"ACTION_ARGUMENT_HTML_ELEMENT_STRING";
@@ -355,12 +363,14 @@
* or to move it otherwise.
* <p>
* <strong>Type:</strong> boolean<br>
- * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
- * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
- * </p>
+ * <strong>Actions:</strong>
+ * <ul>
+ * <li>{@link AccessibilityAction#ACTION_NEXT_AT_MOVEMENT_GRANULARITY}</li>
+ * <li>{@link AccessibilityAction#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}</li>
+ * </ul>
*
- * @see #ACTION_NEXT_AT_MOVEMENT_GRANULARITY
- * @see #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ * @see AccessibilityAction#ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ * @see AccessibilityAction#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
*/
public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN =
"ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
@@ -369,10 +379,12 @@
* Argument for specifying the selection start.
* <p>
* <strong>Type:</strong> int<br>
- * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION}
- * </p>
+ * <strong>Actions:</strong>
+ * <ul>
+ * <li>{@link AccessibilityAction#ACTION_SET_SELECTION}</li>
+ * </ul>
*
- * @see #ACTION_SET_SELECTION
+ * @see AccessibilityAction#ACTION_SET_SELECTION
*/
public static final String ACTION_ARGUMENT_SELECTION_START_INT =
"ACTION_ARGUMENT_SELECTION_START_INT";
@@ -381,26 +393,58 @@
* Argument for specifying the selection end.
* <p>
* <strong>Type:</strong> int<br>
- * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION}
- * </p>
+ * <strong>Actions:</strong>
+ * <ul>
+ * <li>{@link AccessibilityAction#ACTION_SET_SELECTION}</li>
+ * </ul>
*
- * @see #ACTION_SET_SELECTION
+ * @see AccessibilityAction#ACTION_SET_SELECTION
*/
public static final String ACTION_ARGUMENT_SELECTION_END_INT =
"ACTION_ARGUMENT_SELECTION_END_INT";
/**
- * Argument for specifying the text content to set
+ * Argument for specifying the text content to set.
* <p>
* <strong>Type:</strong> CharSequence<br>
- * <strong>Actions:</strong> {@link #ACTION_SET_TEXT}
- * </p>
+ * <strong>Actions:</strong>
+ * <ul>
+ * <li>{@link AccessibilityAction#ACTION_SET_TEXT}</li>
+ * </ul>
*
- * @see #ACTION_SET_TEXT
+ * @see AccessibilityAction#ACTION_SET_TEXT
*/
public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE =
"ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
+ /**
+ * Argument for specifying the collection row to make visible on screen.
+ * <p>
+ * <strong>Type:</strong> int<br>
+ * <strong>Actions:</strong>
+ * <ul>
+ * <li>{@link AccessibilityAction#ACTION_SCROLL_TO_POSITION}</li>
+ * </ul>
+ *
+ * @see AccessibilityAction#ACTION_SCROLL_TO_POSITION
+ */
+ public static final String ACTION_ARGUMENT_ROW_INT =
+ "android.view.accessibility.action.ARGUMENT_ROW_INT";
+
+ /**
+ * Argument for specifying the collection column to make visible on screen.
+ * <p>
+ * <strong>Type:</strong> int<br>
+ * <strong>Actions:</strong>
+ * <ul>
+ * <li>{@link AccessibilityAction#ACTION_SCROLL_TO_POSITION}</li>
+ * </ul>
+ *
+ * @see AccessibilityAction#ACTION_SCROLL_TO_POSITION
+ */
+ public static final String ACTION_ARGUMENT_COLUMN_INT =
+ "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
+
// Focus types
/**
@@ -539,7 +583,7 @@
// Housekeeping.
private static final int MAX_POOL_SIZE = 50;
private static final SynchronizedPool<AccessibilityNodeInfo> sPool =
- new SynchronizedPool<AccessibilityNodeInfo>(MAX_POOL_SIZE);
+ new SynchronizedPool<>(MAX_POOL_SIZE);
private boolean mSealed;
@@ -970,7 +1014,7 @@
}
if (mActions == null) {
- mActions = new ArrayList<AccessibilityAction>();
+ mActions = new ArrayList<>();
}
mActions.remove(action);
@@ -3411,9 +3455,24 @@
* @see View#requestRectangleOnScreen(Rect)
*/
public static final AccessibilityAction ACTION_SHOW_ON_SCREEN =
- new AccessibilityAction(R.id.accessibility_action_show_on_screen, null);
+ new AccessibilityAction(R.id.accessibilityActionShowOnScreen, null);
- private static final ArraySet<AccessibilityAction> sStandardActions = new ArraySet<AccessibilityAction>();
+ /**
+ * Action that scrolls the node to make the specified collection
+ * position visible on screen.
+ * <p>
+ * <strong>Arguments:</strong>
+ * <ul>
+ * <li>{@link AccessibilityNodeInfo#ACTION_ARGUMENT_ROW_INT}</li>
+ * <li>{@link AccessibilityNodeInfo#ACTION_ARGUMENT_COLUMN_INT}</li>
+ * <ul>
+ *
+ * @see AccessibilityNodeInfo#getCollectionInfo()
+ */
+ public static final AccessibilityAction ACTION_SCROLL_TO_POSITION =
+ new AccessibilityAction(R.id.accessibilityActionScrollToPosition, null);
+
+ private static final ArraySet<AccessibilityAction> sStandardActions = new ArraySet<>();
static {
sStandardActions.add(ACTION_FOCUS);
sStandardActions.add(ACTION_CLEAR_FOCUS);
@@ -3438,6 +3497,7 @@
sStandardActions.add(ACTION_DISMISS);
sStandardActions.add(ACTION_SET_TEXT);
sStandardActions.add(ACTION_SHOW_ON_SCREEN);
+ sStandardActions.add(ACTION_SCROLL_TO_POSITION);
}
private final int mActionId;
@@ -3658,7 +3718,7 @@
private static final int MAX_POOL_SIZE = 20;
private static final SynchronizedPool<CollectionInfo> sPool =
- new SynchronizedPool<CollectionInfo>(MAX_POOL_SIZE);
+ new SynchronizedPool<>(MAX_POOL_SIZE);
private int mRowCount;
private int mColumnCount;
@@ -3804,7 +3864,7 @@
private static final int MAX_POOL_SIZE = 20;
private static final SynchronizedPool<CollectionItemInfo> sPool =
- new SynchronizedPool<CollectionItemInfo>(MAX_POOL_SIZE);
+ new SynchronizedPool<>(MAX_POOL_SIZE);
/**
* Obtains a pooled instance that is a clone of another one.
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index aa77d5e..7ab5aaa 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -40,6 +40,7 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewAssistStructure;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
@@ -2424,6 +2425,11 @@
return WebView.class.getName();
}
+ @Override
+ public void onProvideVirtualAssistStructure(ViewAssistStructure structure) {
+ mProvider.getViewDelegate().onProvideVirtualAssistStructure(structure);
+ }
+
/** @hide */
@Override
public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index fa2ce1b..d5787de 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -32,6 +32,7 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewAssistStructure;
import android.view.ViewGroup.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -298,6 +299,8 @@
interface ViewDelegate {
public boolean shouldDelayChildPressedState();
+ public void onProvideVirtualAssistStructure(ViewAssistStructure structure);
+
public AccessibilityNodeProvider getAccessibilityNodeProvider();
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info);
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 168066a..e7b6238 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -61,6 +61,7 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
@@ -1504,11 +1505,11 @@
super.onInitializeAccessibilityNodeInfoInternal(info);
if (isEnabled()) {
if (canScrollUp()) {
- info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+ info.addAction(AccessibilityAction.ACTION_SCROLL_BACKWARD);
info.setScrollable(true);
}
if (canScrollDown()) {
- info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+ info.addAction(AccessibilityAction.ACTION_SCROLL_FORWARD);
info.setScrollable(true);
}
}
@@ -2502,18 +2503,18 @@
if (position == getSelectedItemPosition()) {
info.setSelected(true);
- info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_SELECTION);
+ info.addAction(AccessibilityAction.ACTION_CLEAR_SELECTION);
} else {
- info.addAction(AccessibilityNodeInfo.ACTION_SELECT);
+ info.addAction(AccessibilityAction.ACTION_SELECT);
}
if (isClickable()) {
- info.addAction(AccessibilityNodeInfo.ACTION_CLICK);
+ info.addAction(AccessibilityAction.ACTION_CLICK);
info.setClickable(true);
}
if (isLongClickable()) {
- info.addAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);
+ info.addAction(AccessibilityAction.ACTION_LONG_CLICK);
info.setLongClickable(true);
}
}
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index c6e3dc8..be0ca71 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -21,6 +21,7 @@
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Rect;
+import android.os.Bundle;
import android.os.Trace;
import android.util.AttributeSet;
import android.util.MathUtils;
@@ -32,12 +33,15 @@
import android.view.ViewGroup;
import android.view.ViewRootImpl;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo;
import android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo;
import android.view.animation.GridLayoutAnimationController;
import android.widget.RemoteViews.RemoteView;
+import com.android.internal.R;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -117,7 +121,7 @@
}
public GridView(Context context, AttributeSet attrs) {
- this(context, attrs, com.android.internal.R.attr.gridViewStyle);
+ this(context, attrs, R.attr.gridViewStyle);
}
public GridView(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -128,30 +132,30 @@
super(context, attrs, defStyleAttr, defStyleRes);
final TypedArray a = context.obtainStyledAttributes(
- attrs, com.android.internal.R.styleable.GridView, defStyleAttr, defStyleRes);
+ attrs, R.styleable.GridView, defStyleAttr, defStyleRes);
int hSpacing = a.getDimensionPixelOffset(
- com.android.internal.R.styleable.GridView_horizontalSpacing, 0);
+ R.styleable.GridView_horizontalSpacing, 0);
setHorizontalSpacing(hSpacing);
int vSpacing = a.getDimensionPixelOffset(
- com.android.internal.R.styleable.GridView_verticalSpacing, 0);
+ R.styleable.GridView_verticalSpacing, 0);
setVerticalSpacing(vSpacing);
- int index = a.getInt(com.android.internal.R.styleable.GridView_stretchMode, STRETCH_COLUMN_WIDTH);
+ int index = a.getInt(R.styleable.GridView_stretchMode, STRETCH_COLUMN_WIDTH);
if (index >= 0) {
setStretchMode(index);
}
- int columnWidth = a.getDimensionPixelOffset(com.android.internal.R.styleable.GridView_columnWidth, -1);
+ int columnWidth = a.getDimensionPixelOffset(R.styleable.GridView_columnWidth, -1);
if (columnWidth > 0) {
setColumnWidth(columnWidth);
}
- int numColumns = a.getInt(com.android.internal.R.styleable.GridView_numColumns, 1);
+ int numColumns = a.getInt(R.styleable.GridView_numColumns, 1);
setNumColumns(numColumns);
- index = a.getInt(com.android.internal.R.styleable.GridView_gravity, -1);
+ index = a.getInt(R.styleable.GridView_gravity, -1);
if (index >= 0) {
setGravity(index);
}
@@ -2356,6 +2360,36 @@
final CollectionInfo collectionInfo = CollectionInfo.obtain(
rowsCount, columnsCount, false, selectionMode);
info.setCollectionInfo(collectionInfo);
+
+ if (columnsCount > 0 || rowsCount > 0) {
+ info.addAction(AccessibilityAction.ACTION_SCROLL_TO_POSITION);
+ }
+ }
+
+ /** @hide */
+ @Override
+ public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
+ if (super.performAccessibilityActionInternal(action, arguments)) {
+ return true;
+ }
+
+ switch (action) {
+ case R.id.accessibilityActionScrollToPosition: {
+ // GridView only supports scrolling in one direction, so we can
+ // ignore the column argument.
+ final int numColumns = getNumColumns();
+ final int row = arguments.getInt(AccessibilityNodeInfo.ACTION_ARGUMENT_ROW_INT, -1);
+ final int position = Math.min(row * numColumns, getCount() - 1);
+ if (row >= 0) {
+ // The accessibility service gets data asynchronously, so
+ // we'll be a little lenient by clamping the last position.
+ smoothScrollToPosition(position);
+ return true;
+ }
+ } break;
+ }
+
+ return false;
}
@Override
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index ee2c055..def5929 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.os.Bundle;
import android.os.Trace;
import com.android.internal.R;
import com.android.internal.util.Predicate;
@@ -42,6 +43,7 @@
import android.view.ViewParent;
import android.view.ViewRootImpl;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo;
import android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo;
import android.view.accessibility.AccessibilityNodeProvider;
@@ -3893,6 +3895,33 @@
final CollectionInfo collectionInfo = CollectionInfo.obtain(
rowsCount, 1, false, selectionMode);
info.setCollectionInfo(collectionInfo);
+
+ if (rowsCount > 0) {
+ info.addAction(AccessibilityAction.ACTION_SCROLL_TO_POSITION);
+ }
+ }
+
+ /** @hide */
+ @Override
+ public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
+ if (super.performAccessibilityActionInternal(action, arguments)) {
+ return true;
+ }
+
+ switch (action) {
+ case R.id.accessibilityActionScrollToPosition: {
+ final int row = arguments.getInt(AccessibilityNodeInfo.ACTION_ARGUMENT_ROW_INT, -1);
+ final int position = Math.min(row, getCount() - 1);
+ if (row >= 0) {
+ // The accessibility service gets data asynchronously, so
+ // we'll be a little lenient by clamping the last position.
+ smoothScrollToPosition(position);
+ return true;
+ }
+ } break;
+ }
+
+ return false;
}
@Override
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 50d701a..16353e8 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -316,7 +316,7 @@
mProgressTintInfo = new ProgressTintInfo();
}
mProgressTintInfo.mProgressTintMode = Drawable.parseTintMode(a.getInt(
- R.styleable.ProgressBar_progressBackgroundTintMode, -1), null);
+ R.styleable.ProgressBar_progressTintMode, -1), null);
mProgressTintInfo.mHasProgressTintMode = true;
}
@@ -334,7 +334,7 @@
mProgressTintInfo = new ProgressTintInfo();
}
mProgressTintInfo.mProgressBackgroundTintMode = Drawable.parseTintMode(a.getInt(
- R.styleable.ProgressBar_progressTintMode, -1), null);
+ R.styleable.ProgressBar_progressBackgroundTintMode, -1), null);
mProgressTintInfo.mHasProgressBackgroundTintMode = true;
}
@@ -365,7 +365,7 @@
mProgressTintInfo.mHasSecondaryProgressTint = true;
}
- if (a.hasValue(R.styleable.ProgressBar_indeterminateTint)) {
+ if (a.hasValue(R.styleable.ProgressBar_indeterminateTintMode)) {
if (mProgressTintInfo == null) {
mProgressTintInfo = new ProgressTintInfo();
}
diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java
index 143dea4..52e1728 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -79,8 +79,10 @@
// Transparent alpha level
private static final int ALPHA_TRANSPARENT = 0;
- private static final int DEGREES_FOR_ONE_HOUR = 30;
- private static final int DEGREES_FOR_ONE_MINUTE = 6;
+ private static final int HOURS_IN_DAY = 24;
+ private static final int MINUTES_IN_HOUR = 60;
+ private static final int DEGREES_FOR_ONE_HOUR = 360 / HOURS_IN_DAY;
+ private static final int DEGREES_FOR_ONE_MINUTE = 360 / MINUTES_IN_HOUR;
private static final int[] HOURS_NUMBERS = {12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
private static final int[] HOURS_NUMBERS_24 = {0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23};
@@ -140,8 +142,7 @@
private final float[] mInnerTextX = new float[12];
private final float[] mInnerTextY = new float[12];
- private final int[] mLineLength = new int[3];
- private final int[] mSelectionDegrees = new int[3];
+ private final int[] mSelectionDegrees = new int[2];
private final ArrayList<Animator> mHoursToMinutesAnims = new ArrayList<>();
private final ArrayList<Animator> mMinuteToHoursAnims = new ArrayList<>();
@@ -168,13 +169,13 @@
private int mYCenter;
private int mCircleRadius;
- private int mMinHypotenuseForInnerNumber;
- private int mMaxHypotenuseForOuterNumber;
- private int mHalfwayHypotenusePoint;
+ private int mMinDistForInnerNumber;
+ private int mMaxDistForOuterNumber;
+ private int mHalfwayDist;
private String[] mOuterTextHours;
private String[] mInnerTextHours;
- private String[] mOuterTextMinutes;
+ private String[] mMinutesText;
private AnimatorSet mTransition;
private int mAmOrPm;
@@ -462,11 +463,10 @@
private void setCurrentHourInternal(int hour, boolean callback, boolean autoAdvance) {
final int degrees = (hour % 12) * DEGREES_FOR_ONE_HOUR;
mSelectionDegrees[HOURS] = degrees;
- mSelectionDegrees[HOURS_INNER] = degrees;
// 0 is 12 AM (midnight) and 12 is 12 PM (noon).
final int amOrPm = (hour == 0 || (hour % 24) < 12) ? AM : PM;
- final boolean isOnInnerCircle = mIs24HourMode && hour >= 1 && hour <= 12;
+ final boolean isOnInnerCircle = getInnerCircleForHour(hour);
if (mAmOrPm != amOrPm || mIsOnInnerCircle != isOnInnerCircle) {
mAmOrPm = amOrPm;
mIsOnInnerCircle = isOnInnerCircle;
@@ -488,8 +488,7 @@
* @return the current hour between 0 and 23 (inclusive)
*/
public int getCurrentHour() {
- return getHourForDegrees(
- mSelectionDegrees[mIsOnInnerCircle ? HOURS_INNER : HOURS], mIsOnInnerCircle);
+ return getHourForDegrees(mSelectionDegrees[HOURS], mIsOnInnerCircle);
}
private int getHourForDegrees(int degrees, boolean innerCircle) {
@@ -497,11 +496,11 @@
if (mIs24HourMode) {
// Convert the 12-hour value into 24-hour time based on where the
// selector is positioned.
- if (innerCircle && hour == 0) {
- // Inner circle is 1 through 12.
+ if (!innerCircle && hour == 0) {
+ // Outer circle is 1 through 12.
hour = 12;
- } else if (!innerCircle && hour != 0) {
- // Outer circle is 13 through 23 and 0.
+ } else if (innerCircle && hour != 0) {
+ // Inner circle is 13 through 23 and 0.
hour += 12;
}
} else if (mAmOrPm == PM) {
@@ -510,6 +509,9 @@
return hour;
}
+ /**
+ * @param hour the hour in 24-hour time or 12-hour time
+ */
private int getDegreesForHour(int hour) {
// Convert to be 0-11.
if (mIs24HourMode) {
@@ -522,12 +524,19 @@
return hour * DEGREES_FOR_ONE_HOUR;
}
+ /**
+ * @param hour the hour in 24-hour time or 12-hour time
+ */
+ private boolean getInnerCircleForHour(int hour) {
+ return mIs24HourMode && (hour == 0 || hour > 12);
+ }
+
public void setCurrentMinute(int minute) {
setCurrentMinuteInternal(minute, true);
}
private void setCurrentMinuteInternal(int minute, boolean callback) {
- mSelectionDegrees[MINUTES] = (minute % 60) * DEGREES_FOR_ONE_MINUTE;
+ mSelectionDegrees[MINUTES] = (minute % MINUTES_IN_HOUR) * DEGREES_FOR_ONE_MINUTE;
invalidate();
@@ -572,6 +581,7 @@
initData();
invalidate();
+ mTouchHelper.invalidateRoot();
}
public void showMinutes(boolean animate) {
@@ -587,6 +597,7 @@
initData();
invalidate();
+ mTouchHelper.invalidateRoot();
}
private void initHoursAndMinutesText() {
@@ -608,7 +619,7 @@
mInnerTextHours = mHours12Texts;
}
- mOuterTextMinutes = mMinutesTexts;
+ mMinutesText = mMinutesTexts;
final int hoursAlpha = mShowHours ? ALPHA_OPAQUE : ALPHA_TRANSPARENT;
mAlpha[HOURS].setValue(hoursAlpha);
@@ -627,9 +638,9 @@
mYCenter = getHeight() / 2;
mCircleRadius = Math.min(mXCenter, mYCenter);
- mMinHypotenuseForInnerNumber = mCircleRadius - mTextInset[HOURS_INNER] - mSelectorRadius;
- mMaxHypotenuseForOuterNumber = mCircleRadius - mTextInset[HOURS] + mSelectorRadius;
- mHalfwayHypotenusePoint = mCircleRadius - (mTextInset[HOURS] + mTextInset[HOURS_INNER]) / 2;
+ mMinDistForInnerNumber = mCircleRadius - mTextInset[HOURS_INNER] - mSelectorRadius;
+ mMaxDistForOuterNumber = mCircleRadius - mTextInset[HOURS] + mSelectorRadius;
+ mHalfwayDist = mCircleRadius - (mTextInset[HOURS] + mTextInset[HOURS_INNER]) / 2;
calculatePositionsHours();
calculatePositionsMinutes();
@@ -674,6 +685,7 @@
private void drawMinutes(Canvas canvas, float alphaMod) {
final int minutesAlpha = (int) (mAlpha[MINUTES].getValue() * alphaMod + 0.5f);
if (minutesAlpha > 0) {
+ // Draw the minute selector under the elements.
drawSelector(canvas, MINUTES, mSelectorPath, alphaMod);
// Exclude the selector region, then draw minutes with no
@@ -681,7 +693,7 @@
canvas.save(Canvas.CLIP_SAVE_FLAG);
canvas.clipPath(mSelectorPath, Region.Op.DIFFERENCE);
drawTextElements(canvas, mTextSize[MINUTES], mTypeface, mTextColor[MINUTES],
- mOuterTextMinutes, mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES],
+ mMinutesText, mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES],
minutesAlpha, false, 0, false);
canvas.restore();
@@ -690,7 +702,7 @@
canvas.save(Canvas.CLIP_SAVE_FLAG);
canvas.clipPath(mSelectorPath, Region.Op.INTERSECT);
drawTextElements(canvas, mTextSize[MINUTES], mTypeface, mTextColor[MINUTES],
- mOuterTextMinutes, mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES],
+ mMinutesText, mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES],
minutesAlpha, true, mSelectionDegrees[MINUTES], true);
canvas.restore();
}
@@ -718,7 +730,7 @@
// Calculate the current radius at which to place the selection circle.
final int selRadius = mSelectorRadius;
final int selLength = mCircleRadius - mTextInset[index];
- final double selAngleRad = Math.toRadians(mSelectionDegrees[index]);
+ final double selAngleRad = Math.toRadians(mSelectionDegrees[index % 2]);
final float selCenterX = mXCenter + selLength * (float) Math.sin(selAngleRad);
final float selCenterY = mYCenter - selLength * (float) Math.cos(selAngleRad);
@@ -734,10 +746,10 @@
}
// Draw the dot if we're between two items.
- final boolean shouldDrawDot = mSelectionDegrees[index] % 30 != 0;
+ final boolean shouldDrawDot = mSelectionDegrees[index % 2] % 30 != 0;
if (shouldDrawDot) {
final Paint dotPaint = mPaintSelector[index % 2][SELECTOR_DOT];
- dotPaint.setColor(color);
+ dotPaint.setColor(mSelectorDotColor);
canvas.drawCircle(selCenterX, selCenterY, mSelectorDotRadius, dotPaint);
}
@@ -898,56 +910,43 @@
}
private int getDegreesFromXY(float x, float y, boolean constrainOutside) {
- final double hypotenuse = Math.sqrt(
- (y - mYCenter) * (y - mYCenter) + (x - mXCenter) * (x - mXCenter));
+ // Ensure the point is inside the touchable area.
+ final int innerBound;
+ final int outerBound;
+ if (mIs24HourMode && mShowHours) {
+ innerBound = mMinDistForInnerNumber;
+ outerBound = mMaxDistForOuterNumber;
+ } else {
+ final int index = mShowHours ? HOURS : MINUTES;
+ final int center = mCircleRadius - mTextInset[index];
+ innerBound = center - mSelectorRadius;
+ outerBound = center + mSelectorRadius;
+ }
- // Basic check if we're outside the range of the disk
- if (constrainOutside && hypotenuse > mCircleRadius) {
+ final double dX = x - mXCenter;
+ final double dY = y - mYCenter;
+ final double distFromCenter = Math.sqrt(dX * dX + dY * dY);
+ if (distFromCenter < innerBound || constrainOutside && distFromCenter > outerBound) {
return -1;
}
- // Check
+ // Convert to degrees.
+ final int degrees = (int) (Math.toDegrees(Math.atan2(dY, dX) + Math.PI / 2) + 0.5);
+ if (degrees < 0) {
+ return degrees + 360;
+ } else {
+ return degrees;
+ }
+ }
+
+ private boolean getInnerCircleFromXY(float x, float y) {
if (mIs24HourMode && mShowHours) {
- if (hypotenuse >= mMinHypotenuseForInnerNumber
- && hypotenuse <= mHalfwayHypotenusePoint) {
- mIsOnInnerCircle = true;
- } else if ((hypotenuse <= mMaxHypotenuseForOuterNumber || !constrainOutside)
- && hypotenuse >= mHalfwayHypotenusePoint) {
- mIsOnInnerCircle = false;
- } else {
- return -1;
- }
- } else {
- final int index = (mShowHours) ? HOURS : MINUTES;
- final float length = (mCircleRadius - mTextInset[index]);
- final int distanceToNumber = (int) (hypotenuse - length);
- final int maxAllowedDistance = mTextInset[index];
- if (distanceToNumber < -maxAllowedDistance
- || (constrainOutside && distanceToNumber > maxAllowedDistance)) {
- return -1;
- }
+ final double dX = x - mXCenter;
+ final double dY = y - mYCenter;
+ final double distFromCenter = Math.sqrt(dX * dX + dY * dY);
+ return distFromCenter <= mHalfwayDist;
}
-
- final float opposite = Math.abs(y - mYCenter);
- int degrees = (int) (Math.toDegrees(Math.asin(opposite / hypotenuse)) + 0.5);
-
- // Now we have to translate to the correct quadrant.
- final boolean rightSide = (x > mXCenter);
- final boolean topSide = (y < mYCenter);
- if (rightSide) {
- if (topSide) {
- degrees = 90 - degrees;
- } else {
- degrees = 90 + degrees;
- }
- } else {
- if (topSide) {
- degrees = 270 + degrees;
- } else {
- degrees = 270 - degrees;
- }
- }
- return degrees;
+ return false;
}
boolean mChangedDuringTouch = false;
@@ -987,34 +986,28 @@
private boolean handleTouchInput(
float x, float y, boolean forceSelection, boolean autoAdvance) {
- // Calling getDegreesFromXY has side effects, so cache
- // whether we used to be on the inner circle.
- final boolean wasOnInnerCircle = mIsOnInnerCircle;
+ final boolean isOnInnerCircle = getInnerCircleFromXY(x, y);
final int degrees = getDegreesFromXY(x, y, false);
if (degrees == -1) {
return false;
}
- final int[] selectionDegrees = mSelectionDegrees;
final int type;
final int newValue;
final boolean valueChanged;
if (mShowHours) {
final int snapDegrees = snapOnly30s(degrees, 0) % 360;
- valueChanged = selectionDegrees[HOURS] != snapDegrees
- || selectionDegrees[HOURS_INNER] != snapDegrees
- || wasOnInnerCircle != mIsOnInnerCircle;
-
- selectionDegrees[HOURS] = snapDegrees;
- selectionDegrees[HOURS_INNER] = snapDegrees;
+ valueChanged = mIsOnInnerCircle != isOnInnerCircle
+ || mSelectionDegrees[HOURS] != snapDegrees;
+ mIsOnInnerCircle = isOnInnerCircle;
+ mSelectionDegrees[HOURS] = snapDegrees;
type = HOURS;
newValue = getCurrentHour();
} else {
final int snapDegrees = snapPrefer30s(degrees) % 360;
- valueChanged = selectionDegrees[MINUTES] != snapDegrees;
-
- selectionDegrees[MINUTES] = snapDegrees;
+ valueChanged = mSelectionDegrees[MINUTES] != snapDegrees;
+ mSelectionDegrees[MINUTES] = snapDegrees;
type = MINUTES;
newValue = getCurrentMinute();
}
@@ -1132,17 +1125,11 @@
@Override
protected int getVirtualViewAt(float x, float y) {
final int id;
-
- // Calling getDegreesXY() has side-effects, so we need to cache the
- // current inner circle value and restore after the call.
- final boolean wasOnInnerCircle = mIsOnInnerCircle;
final int degrees = getDegreesFromXY(x, y, true);
- final boolean isOnInnerCircle = mIsOnInnerCircle;
- mIsOnInnerCircle = wasOnInnerCircle;
-
if (degrees != -1) {
final int snapDegrees = snapOnly30s(degrees, 0) % 360;
if (mShowHours) {
+ final boolean isOnInnerCircle = getInnerCircleFromXY(x, y);
final int hour24 = getHourForDegrees(snapDegrees, isOnInnerCircle);
final int hour = mIs24HourMode ? hour24 : hour24To12(hour24);
id = makeId(TYPE_HOUR, hour);
@@ -1153,8 +1140,10 @@
// If the touched minute is closer to the current minute
// than it is to the snapped minute, return current.
+ final int currentOffset = getCircularDiff(current, touched, MINUTES_IN_HOUR);
+ final int snappedOffset = getCircularDiff(snapped, touched, MINUTES_IN_HOUR);
final int minute;
- if (Math.abs(current - touched) < Math.abs(snapped - touched)) {
+ if (currentOffset < snappedOffset) {
minute = current;
} else {
minute = snapped;
@@ -1168,6 +1157,20 @@
return id;
}
+ /**
+ * Returns the difference in degrees between two values along a circle.
+ *
+ * @param first value in the range [0,max]
+ * @param second value in the range [0,max]
+ * @param max the maximum value along the circle
+ * @return the difference in between the two values
+ */
+ private int getCircularDiff(int first, int second, int max) {
+ final int diff = Math.abs(first - second);
+ final int midpoint = max / 2;
+ return (diff > midpoint) ? (max - diff) : diff;
+ }
+
@Override
protected void getVisibleVirtualViews(IntArray virtualViewIds) {
if (mShowHours) {
@@ -1178,7 +1181,7 @@
}
} else {
final int current = getCurrentMinute();
- for (int i = 0; i < 60; i += MINUTE_INCREMENT) {
+ for (int i = 0; i < MINUTES_IN_HOUR; i += MINUTE_INCREMENT) {
virtualViewIds.add(makeId(TYPE_MINUTE, i));
// If the current minute falls between two increments,
@@ -1236,7 +1239,7 @@
if (value < current && nextValue > current) {
// The current value is between two snap values.
return makeId(type, current);
- } else if (nextValue < 60) {
+ } else if (nextValue < MINUTES_IN_HOUR) {
return makeId(type, nextValue);
}
}
@@ -1290,7 +1293,7 @@
final float centerRadius;
final float degrees;
if (type == TYPE_HOUR) {
- final boolean innerCircle = mIs24HourMode && value > 0 && value <= 12;
+ final boolean innerCircle = getInnerCircleForHour(value);
if (innerCircle) {
centerRadius = mCircleRadius - mTextInset[HOURS_INNER];
radius = mSelectorRadius;
diff --git a/core/java/com/android/internal/app/DumpHeapActivity.java b/core/java/com/android/internal/app/DumpHeapActivity.java
index 7e70b0c..0ce501e 100644
--- a/core/java/com/android/internal/app/DumpHeapActivity.java
+++ b/core/java/com/android/internal/app/DumpHeapActivity.java
@@ -17,13 +17,16 @@
package com.android.internal.app;
import android.app.Activity;
+import android.app.ActivityManager;
import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.DebugUtils;
+import android.util.Slog;
/**
* This activity is displayed when the system has collected a heap dump from
@@ -34,6 +37,8 @@
public static final String KEY_PROCESS = "process";
/** The size limit the process reached */
public static final String KEY_SIZE = "size";
+ /** Optional name of package to directly launch */
+ public static final String KEY_DIRECT_LAUNCH = "direct_launch";
// Broadcast action to determine when to delete the current dump heap data.
public static final String ACTION_DELETE_DUMPHEAP = "com.android.server.am.DELETE_DUMPHEAP";
@@ -54,6 +59,28 @@
mProcess = getIntent().getStringExtra(KEY_PROCESS);
mSize = getIntent().getLongExtra(KEY_SIZE, 0);
+
+ String directLaunch = getIntent().getStringExtra(KEY_DIRECT_LAUNCH);
+ if (directLaunch != null) {
+ Intent intent = new Intent(ActivityManager.ACTION_REPORT_HEAP_LIMIT);
+ intent.setPackage(directLaunch);
+ ClipData clip = ClipData.newUri(getContentResolver(), "Heap Dump", JAVA_URI);
+ intent.setClipData(clip);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.setType(clip.getDescription().getMimeType(0));
+ intent.putExtra(Intent.EXTRA_STREAM, JAVA_URI);
+ try {
+ startActivity(intent);
+ scheduleDelete();
+ mHandled = true;
+ finish();
+ return;
+ } catch (ActivityNotFoundException e) {
+ Slog.i("DumpHeapActivity", "Unable to direct launch to " + directLaunch
+ + ": " + e.getMessage());
+ }
+ }
+
AlertDialog.Builder b = new AlertDialog.Builder(this,
android.R.style.Theme_Material_Light_Dialog_Alert);
b.setTitle(com.android.internal.R.string.dump_heap_title);
@@ -71,9 +98,7 @@
@Override
public void onClick(DialogInterface dialog, int which) {
mHandled = true;
- Intent broadcast = new Intent(ACTION_DELETE_DUMPHEAP);
- broadcast.putExtra(EXTRA_DELAY_DELETE, true);
- sendBroadcast(broadcast);
+ scheduleDelete();
Intent intent = new Intent(Intent.ACTION_SEND);
ClipData clip = ClipData.newUri(getContentResolver(), "Heap Dump", JAVA_URI);
intent.setClipData(clip);
@@ -88,6 +113,12 @@
mDialog = b.show();
}
+ void scheduleDelete() {
+ Intent broadcast = new Intent(ACTION_DELETE_DUMPHEAP);
+ broadcast.putExtra(EXTRA_DELAY_DELETE, true);
+ sendBroadcast(broadcast);
+ }
+
@Override
protected void onStop() {
super.onStop();
diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java
index 1f0bb76..4efefa9 100644
--- a/core/java/com/android/internal/app/LocalePicker.java
+++ b/core/java/com/android/internal/app/LocalePicker.java
@@ -246,9 +246,8 @@
IActivityManager am = ActivityManagerNative.getDefault();
Configuration config = am.getConfiguration();
- // Will set userSetLocale to indicate this isn't some passing default - the user
- // wants this remembered
config.setLocale(locale);
+ config.userSetLocale = true;
am.updateConfiguration(config);
// Trigger the dirty bit for the Settings Provider.
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index 7bdb4be..255f1fd 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -25,15 +25,16 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageParser.PackageLite;
import android.os.Environment;
-import android.os.Environment.UserEnvironment;
import android.os.FileUtils;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.UserHandle;
import android.os.storage.IMountService;
import android.os.storage.StorageManager;
import android.os.storage.StorageResultCode;
+import android.os.storage.StorageVolume;
+import android.os.storage.VolumeInfo;
+import android.util.ArraySet;
import android.util.Log;
import libcore.io.IoUtils;
@@ -335,6 +336,94 @@
/**
* Given a requested {@link PackageInfo#installLocation} and calculated
+ * install size, pick the actual volume to install the app. Only considers
+ * internal and private volumes, and prefers to keep an existing package on
+ * its current volume.
+ *
+ * @return the {@link VolumeInfo#fsUuid} to install onto, or {@code null}
+ * for internal storage.
+ */
+ public static String resolveInstallVolume(Context context, String packageName,
+ int installLocation, long sizeBytes) throws IOException {
+ // TODO: handle existing apps installed in ASEC; currently assumes
+ // they'll end up back on internal storage
+ ApplicationInfo existingInfo = null;
+ try {
+ existingInfo = context.getPackageManager().getApplicationInfo(packageName,
+ PackageManager.GET_UNINSTALLED_PACKAGES);
+ } catch (NameNotFoundException ignored) {
+ }
+
+ final StorageManager storageManager = context.getSystemService(StorageManager.class);
+ final boolean fitsOnInternal = fitsOnInternal(context, sizeBytes);
+
+ final ArraySet<String> allCandidates = new ArraySet<>();
+ VolumeInfo bestCandidate = null;
+ long bestCandidateAvailBytes = Long.MIN_VALUE;
+ for (VolumeInfo vol : storageManager.getVolumes()) {
+ if (vol.type == VolumeInfo.TYPE_PRIVATE && vol.state == VolumeInfo.STATE_MOUNTED) {
+ final long availBytes = storageManager.getStorageBytesUntilLow(new File(vol.path));
+ if (availBytes >= sizeBytes) {
+ allCandidates.add(vol.fsUuid);
+ }
+ if (availBytes >= bestCandidateAvailBytes) {
+ bestCandidate = vol;
+ bestCandidateAvailBytes = availBytes;
+ }
+ }
+ }
+
+ // System apps always forced to internal storage
+ if (existingInfo != null && existingInfo.isSystemApp()) {
+ installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;
+ }
+
+ // If app expresses strong desire for internal space, honor it
+ if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
+ if (fitsOnInternal) {
+ return null;
+ } else {
+ throw new IOException("Requested internal only, but not enough space");
+ }
+ }
+
+ // If app already exists somewhere, prefer to stay on that volume
+ if (existingInfo != null) {
+ if (existingInfo.volumeUuid == null && fitsOnInternal) {
+ return null;
+ }
+ if (allCandidates.contains(existingInfo.volumeUuid)) {
+ return existingInfo.volumeUuid;
+ }
+ }
+
+ // We're left with either preferring external or auto, so just pick
+ // volume with most space
+ if (bestCandidate != null) {
+ return bestCandidate.fsUuid;
+ } else if (fitsOnInternal) {
+ return null;
+ } else {
+ throw new IOException("No special requests, but no room anywhere");
+ }
+ }
+
+ public static boolean fitsOnInternal(Context context, long sizeBytes) {
+ final StorageManager storage = context.getSystemService(StorageManager.class);
+ final File target = Environment.getDataDirectory();
+ return (sizeBytes <= storage.getStorageBytesUntilLow(target));
+ }
+
+ public static boolean fitsOnExternal(Context context, long sizeBytes) {
+ final StorageManager storage = context.getSystemService(StorageManager.class);
+ final StorageVolume primary = storage.getPrimaryVolume();
+ return (sizeBytes > 0) && !primary.isEmulated()
+ && Environment.MEDIA_MOUNTED.equals(primary.getState())
+ && sizeBytes <= storage.getStorageBytesUntilLow(primary.getPathFile());
+ }
+
+ /**
+ * Given a requested {@link PackageInfo#installLocation} and calculated
* install size, pick the actual location to install the app.
*/
public static int resolveInstallLocation(Context context, String packageName,
@@ -363,6 +452,7 @@
} else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
// When app is already installed, prefer same medium
if (existingInfo != null) {
+ // TODO: distinguish if this is external ASEC
if ((existingInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
prefer = RECOMMEND_INSTALL_EXTERNAL;
} else {
@@ -377,30 +467,21 @@
checkBoth = false;
}
- final boolean emulated = Environment.isExternalStorageEmulated();
- final StorageManager storage = StorageManager.from(context);
-
boolean fitsOnInternal = false;
if (checkBoth || prefer == RECOMMEND_INSTALL_INTERNAL) {
- final File target = Environment.getDataDirectory();
- fitsOnInternal = (sizeBytes <= storage.getStorageBytesUntilLow(target));
+ fitsOnInternal = fitsOnInternal(context, sizeBytes);
}
boolean fitsOnExternal = false;
- if (!emulated && (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL)) {
- final File target = new UserEnvironment(UserHandle.USER_OWNER)
- .getExternalStorageDirectory();
- // External is only an option when size is known
- if (sizeBytes > 0) {
- fitsOnExternal = (sizeBytes <= storage.getStorageBytesUntilLow(target));
- }
+ if (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL) {
+ fitsOnExternal = fitsOnExternal(context, sizeBytes);
}
if (prefer == RECOMMEND_INSTALL_INTERNAL) {
if (fitsOnInternal) {
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
}
- } else if (!emulated && prefer == RECOMMEND_INSTALL_EXTERNAL) {
+ } else if (prefer == RECOMMEND_INSTALL_EXTERNAL) {
if (fitsOnExternal) {
return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
}
@@ -409,22 +490,12 @@
if (checkBoth) {
if (fitsOnInternal) {
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
- } else if (!emulated && fitsOnExternal) {
+ } else if (fitsOnExternal) {
return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
}
}
- /*
- * If they requested to be on the external media by default, return that
- * the media was unavailable. Otherwise, indicate there was insufficient
- * storage space available.
- */
- if (!emulated && (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL)
- && !Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
- return PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE;
- } else {
- return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
- }
+ return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
}
public static long calculateInstalledSize(PackageLite pkg, boolean isForwardLocked,
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 49d565d..70f7b72 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -32,6 +32,7 @@
import android.system.Os;
import android.system.OsConstants;
import android.system.StructPollfd;
+import android.text.Hyphenator;
import android.util.EventLog;
import android.util.Log;
import android.webkit.WebViewFactory;
@@ -182,6 +183,7 @@
preloadResources();
preloadOpenGL();
preloadSharedLibraries();
+ preloadTextResources();
// Ask the WebViewFactory to do any initialization that must run in the zygote process,
// for memory sharing purposes.
WebViewFactory.prepareWebViewInZygote();
@@ -201,6 +203,10 @@
}
}
+ private static void preloadTextResources() {
+ Hyphenator.init();
+ }
+
/**
* Performs Zygote process initialization. Loads and initializes
* commonly used classes.
diff --git a/core/java/com/android/internal/util/ImageUtils.java b/core/java/com/android/internal/util/ImageUtils.java
index c153904..7d56e9e 100644
--- a/core/java/com/android/internal/util/ImageUtils.java
+++ b/core/java/com/android/internal/util/ImageUtils.java
@@ -17,10 +17,13 @@
package com.android.internal.util;
import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
/**
* Utility class for image analysis and processing.
@@ -117,4 +120,40 @@
&& Math.abs(r - b) < TOLERANCE
&& Math.abs(g - b) < TOLERANCE;
}
+
+ /**
+ * Convert a drawable to a bitmap, scaled to fit within maxWidth and maxHeight.
+ */
+ public static Bitmap buildScaledBitmap(Drawable drawable, int maxWidth,
+ int maxHeight) {
+ if (drawable == null) {
+ return null;
+ }
+ int originalWidth = drawable.getIntrinsicWidth();
+ int originalHeight = drawable.getIntrinsicHeight();
+
+ if ((originalWidth <= maxWidth) && (originalHeight <= maxHeight) &&
+ (drawable instanceof BitmapDrawable)) {
+ return ((BitmapDrawable) drawable).getBitmap();
+ }
+ if (originalHeight <= 0 || originalWidth <= 0) {
+ return null;
+ }
+
+ // create a new bitmap, scaling down to fit the max dimensions of
+ // a large notification icon if necessary
+ float ratio = Math.min((float) maxWidth / (float) originalWidth,
+ (float) maxHeight / (float) originalHeight);
+ ratio = Math.min(1.0f, ratio);
+ int scaledWidth = (int) (ratio * originalWidth);
+ int scaledHeight = (int) (ratio * originalHeight);
+ Bitmap result = Bitmap.createBitmap(scaledWidth, scaledHeight, Config.ARGB_8888);
+
+ // and paint our app bitmap on it
+ Canvas canvas = new Canvas(result);
+ drawable.setBounds(0, 0, scaledWidth, scaledHeight);
+ drawable.draw(canvas);
+
+ return result;
+ }
}
diff --git a/core/java/com/android/server/backup/PreferredActivityBackupHelper.java b/core/java/com/android/server/backup/PreferredActivityBackupHelper.java
new file mode 100644
index 0000000..6ac0d89
--- /dev/null
+++ b/core/java/com/android/server/backup/PreferredActivityBackupHelper.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup;
+
+import android.app.AppGlobals;
+import android.app.backup.BackupDataInputStream;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupHelper;
+import android.content.Context;
+import android.content.pm.IPackageManager;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+import com.android.org.bouncycastle.util.Arrays;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.EOFException;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public class PreferredActivityBackupHelper implements BackupHelper {
+ private static final String TAG = "PreferredBackup";
+ private static final boolean DEBUG = true;
+
+ // current schema of the backup state blob
+ private static final int STATE_VERSION = 1;
+
+ // key under which the preferred-activity state blob is committed to backup
+ private static final String KEY_PREFERRED = "preferred-activity";
+
+ final Context mContext;
+
+ public PreferredActivityBackupHelper(Context context) {
+ mContext = context;
+ }
+
+ // The fds passed here are shared among all helpers, so we mustn't close them
+ private void writeState(ParcelFileDescriptor stateFile, byte[] payload) {
+ try {
+ FileOutputStream fos = new FileOutputStream(stateFile.getFileDescriptor());
+
+ // We explicitly don't close 'out' because we must not close the backing fd.
+ // The FileOutputStream will not close it implicitly.
+ @SuppressWarnings("resource")
+ DataOutputStream out = new DataOutputStream(fos);
+
+ out.writeInt(STATE_VERSION);
+ if (payload == null) {
+ out.writeInt(0);
+ } else {
+ out.writeInt(payload.length);
+ out.write(payload);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to write updated state", e);
+ }
+ }
+
+ private byte[] readState(ParcelFileDescriptor oldStateFd) {
+ FileInputStream fis = new FileInputStream(oldStateFd.getFileDescriptor());
+ BufferedInputStream bis = new BufferedInputStream(fis);
+
+ @SuppressWarnings("resource")
+ DataInputStream in = new DataInputStream(bis);
+
+ byte[] oldState = null;
+ try {
+ int version = in.readInt();
+ if (version == STATE_VERSION) {
+ int size = in.readInt();
+ if (size > 0) {
+ if (size > 200*1024) {
+ Slog.w(TAG, "Suspiciously large state blog; ignoring. N=" + size);
+ } else {
+ // size looks okay; make the return buffer and fill it
+ oldState = new byte[size];
+ in.read(oldState);
+ }
+ }
+ } else {
+ Slog.w(TAG, "Prior state from unrecognized version " + version);
+ }
+ } catch (EOFException e) {
+ // Empty file is expected on first backup, so carry on. If the state
+ // is truncated we just treat it the same way.
+ oldState = null;
+ } catch (Exception e) {
+ Slog.w(TAG, "Error examing prior backup state " + e.getMessage());
+ oldState = null;
+ }
+
+ return oldState;
+ }
+
+ @Override
+ public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+ ParcelFileDescriptor newState) {
+ byte[] payload = null;
+ try {
+ byte[] oldPayload = readState(oldState);
+
+ IPackageManager pm = AppGlobals.getPackageManager();
+ byte[] newPayload = pm.getPreferredActivityBackup(UserHandle.USER_OWNER);
+ if (!Arrays.areEqual(oldPayload, newPayload)) {
+ if (DEBUG) {
+ Slog.i(TAG, "State has changed => writing new preferred app payload");
+ }
+ data.writeEntityHeader(KEY_PREFERRED, newPayload.length);
+ data.writeEntityData(newPayload, newPayload.length);
+ } else {
+ if (DEBUG) {
+ Slog.i(TAG, "No change to state => not writing to wire");
+ }
+ }
+
+ // Always need to re-record the state, even if nothing changed
+ payload = newPayload;
+ } catch (Exception e) {
+ // On failures we'll wind up committing a zero-size state payload. This is
+ // a forward-safe situation because we know we commit the entire new payload
+ // on prior-state mismatch.
+ Slog.w(TAG, "Unable to record preferred activities", e);
+ } finally {
+ writeState(newState, payload);
+ }
+ }
+
+ @Override
+ public void restoreEntity(BackupDataInputStream data) {
+ IPackageManager pm = AppGlobals.getPackageManager();
+ try {
+ byte[] payload = new byte[data.size()];
+ data.read(payload);
+ if (DEBUG) {
+ Slog.i(TAG, "Restoring preferred activities; size=" + payload.length);
+ }
+ pm.restorePreferredActivities(payload, UserHandle.USER_OWNER);
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception reading restore data", e);
+ }
+ }
+
+ @Override
+ public void writeNewStateDescription(ParcelFileDescriptor newState) {
+ writeState(newState, null);
+ }
+
+}
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java
index 037fd66..19d9e29 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -16,7 +16,6 @@
package com.android.server.backup;
-
import android.app.ActivityManagerNative;
import android.app.IWallpaperManager;
import android.app.backup.BackupDataInput;
@@ -43,6 +42,13 @@
public class SystemBackupAgent extends BackupAgentHelper {
private static final String TAG = "SystemBackupAgent";
+ // Names of the helper tags within the dataset. Changing one of these names will
+ // break the ability to restore from datasets that predate the change.
+ private static final String WALLPAPER_HELPER = "wallpaper";
+ private static final String RECENTS_HELPER = "recents";
+ private static final String SYNC_SETTINGS_HELPER = "account_sync_settings";
+ private static final String PREFERRED_HELPER = "preferred_activities";
+
// These paths must match what the WallpaperManagerService uses. The leaf *_FILENAME
// are also used in the full-backup file format, so must not change unless steps are
// taken to support the legacy backed-up datasets.
@@ -84,10 +90,10 @@
Slog.e(TAG, "Couldn't get wallpaper name\n" + re);
}
}
- addHelper("wallpaper", new WallpaperBackupHelper(SystemBackupAgent.this, files, keys));
- addHelper("recents", new RecentsBackupHelper(SystemBackupAgent.this));
- addHelper("account_sync_settings",
- new AccountSyncSettingsBackupHelper(SystemBackupAgent.this));
+ addHelper(WALLPAPER_HELPER, new WallpaperBackupHelper(this, files, keys));
+ addHelper(RECENTS_HELPER, new RecentsBackupHelper(this));
+ addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this));
+ addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper(this));
super.onBackup(oldState, data, newState);
}
@@ -113,15 +119,15 @@
public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
throws IOException {
// On restore, we also support a previous data schema "system_files"
- addHelper("wallpaper", new WallpaperBackupHelper(SystemBackupAgent.this,
+ addHelper(WALLPAPER_HELPER, new WallpaperBackupHelper(this,
new String[] { WALLPAPER_IMAGE, WALLPAPER_INFO },
new String[] { WALLPAPER_IMAGE_KEY, WALLPAPER_INFO_KEY} ));
- addHelper("system_files", new WallpaperBackupHelper(SystemBackupAgent.this,
+ addHelper("system_files", new WallpaperBackupHelper(this,
new String[] { WALLPAPER_IMAGE },
new String[] { WALLPAPER_IMAGE_KEY} ));
- addHelper("recents", new RecentsBackupHelper(SystemBackupAgent.this));
- addHelper("account_sync_settings",
- new AccountSyncSettingsBackupHelper(SystemBackupAgent.this));
+ addHelper(RECENTS_HELPER, new RecentsBackupHelper(this));
+ addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this));
+ addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper(this));
try {
super.onRestore(data, appVersionCode, newState);
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 873b516..4906f59 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -577,7 +577,8 @@
Layout layout;
TypefaceImpl* typeface = getNativeTypeface(env, jpaint);
- MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, textArray, index, count, textLength);
+ MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, textArray + index, 0, count,
+ count);
result = layout.getAdvance();
env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT);
return result;
diff --git a/core/res/res/drawable-hdpi/list_divider_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/list_divider_mtrl_alpha.9.png
deleted file mode 100644
index 2fa6d7e..0000000
--- a/core/res/res/drawable-hdpi/list_divider_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_divider_mtrl_alpha.9.png b/core/res/res/drawable-mdpi/list_divider_mtrl_alpha.9.png
deleted file mode 100644
index 070bdbf..0000000
--- a/core/res/res/drawable-mdpi/list_divider_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/list_divider_mtrl_alpha.9.png b/core/res/res/drawable-xhdpi/list_divider_mtrl_alpha.9.png
deleted file mode 100644
index 0d2836d..0000000
--- a/core/res/res/drawable-xhdpi/list_divider_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/list_divider_mtrl_alpha.9.png b/core/res/res/drawable-xxhdpi/list_divider_mtrl_alpha.9.png
deleted file mode 100644
index b8ac46d..0000000
--- a/core/res/res/drawable-xxhdpi/list_divider_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/ic_check_circle_24px.xml b/core/res/res/drawable/ic_check_circle_24px.xml
new file mode 100644
index 0000000..066a8a7
--- /dev/null
+++ b/core/res/res/drawable/ic_check_circle_24px.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M0 0h24v24H0z"
+ android:fillColor="#00000000"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12.0,2.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0zm-2.0,15.0l-5.0,-5.0 1.41,-1.41L10.0,14.17l7.59,-7.59L19.0,8.0l-9.0,9.0z"/>
+</vector>
diff --git a/core/res/res/drawable/list_divider_material.xml b/core/res/res/drawable/list_divider_material.xml
index babb646..658a59d 100644
--- a/core/res/res/drawable/list_divider_material.xml
+++ b/core/res/res/drawable/list_divider_material.xml
@@ -14,7 +14,10 @@
limitations under the License.
-->
-<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/list_divider_mtrl_alpha"
- android:tint="?attr/colorForeground"
- android:alpha="0.12" />
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:tint="?attr/colorForeground">
+ <solid android:color="#1f000000" />
+ <size
+ android:height="1dp"
+ android:width="1dp" />
+</shape>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 7e963954..aaf252a 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -93,5 +93,10 @@
<item type="id" name="undo" />
<item type="id" name="redo" />
<item type="id" name="replaceText" />
- <item type="id" name="accessibility_action_show_on_screen" />
+
+ <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SHOW_ON_SCREEN}. -->
+ <item type="id" name="accessibilityActionShowOnScreen" />
+
+ <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SCROLL_TO_POSITION}. -->
+ <item type="id" name="accessibilityActionScrollToPosition" />
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index f1707d2..24d17a4 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2646,11 +2646,11 @@
<public type="id" name="undo" />
<public type="id" name="redo" />
<public type="id" name="replaceText" />
+ <public type="id" name="accessibilityActionShowOnScreen" />
+ <public type="id" name="accessibilityActionScrollToPosition" />
<public type="attr" name="allowUndo" />
-
<public type="attr" name="colorBackgroundFloating" />
-
<public type="attr" name="extractNativeLibs" />
<public type="attr" name="usesCleartextTraffic" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4d90932..7dc3ff7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5162,6 +5162,11 @@
<!-- Lock-to-app unlock password string -->
<string name="lock_to_app_unlock_password">Ask for password before unpinning</string>
+ <!-- Notification shown when device owner silently installs a package -->
+ <string name="package_installed_device_owner">Installed by your administrator</string>
+ <!-- Notification shown when device owner silently deletes a package -->
+ <string name="package_deleted_device_owner">Deleted by your administrator</string>
+
<!-- [CHAR_LIMIT=NONE] Battery saver: Feature description -->
<string name="battery_saver_description">To help improve battery life, battery saver reduces your device’s performance and limits vibration, location services, and most background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.\n\nBattery saver turns off automatically when your device is charging.</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 220d5e7..b4ba316 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -694,6 +694,8 @@
<java-symbol type="string" name="lock_to_app_unlock_pin" />
<java-symbol type="string" name="lock_to_app_unlock_pattern" />
<java-symbol type="string" name="lock_to_app_unlock_password" />
+ <java-symbol type="string" name="package_installed_device_owner" />
+ <java-symbol type="string" name="package_deleted_device_owner" />
<java-symbol type="string" name="lockscreen_access_pattern_cell_added" />
<java-symbol type="string" name="lockscreen_access_pattern_cleared" />
<java-symbol type="string" name="lockscreen_access_pattern_detected" />
@@ -1172,6 +1174,7 @@
<java-symbol type="drawable" name="ic_audio_vol" />
<java-symbol type="drawable" name="ic_audio_vol_mute" />
<java-symbol type="drawable" name="ic_bullet_key_permission" />
+ <java-symbol type="drawable" name="ic_check_circle_24px" />
<java-symbol type="drawable" name="ic_contact_picture" />
<java-symbol type="drawable" name="ic_dialog_usb" />
<java-symbol type="drawable" name="ic_emergency" />
@@ -2207,8 +2210,6 @@
<java-symbol type="string" name="storage_sd_card" />
<java-symbol type="string" name="storage_usb" />
- <java-symbol type="id" name="accessibility_action_show_on_screen" />
-
<!-- Floating toolbar -->
<java-symbol type="layout" name="floating_popup_container" />
<java-symbol type="layout" name="floating_popup_menu_button" />
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index 42b5d5d..5c3b90b 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -181,6 +181,18 @@
</family>
<family>
<fileset>
+ <file variant="elegant">NotoSansOriya-Regular.ttf</file>
+ <file variant="elegant">NotoSansOriya-Bold.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file variant="compact">NotoSansOriyaUI-Regular.ttf</file>
+ <file variant="compact">NotoSansOriyaUI-Bold.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
<file>NotoSansSinhala-Regular.ttf</file>
<file>NotoSansSinhala-Bold.ttf</file>
</fileset>
@@ -355,11 +367,6 @@
</family>
<family>
<fileset>
- <file>Lohit-Odia.ttf</file>
- </fileset>
- </family>
- <family>
- <fileset>
<file lang="zh-Hans">NotoSansHans-Regular.otf</file>
</fileset>
</family>
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 37527e9..f3a7647 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -191,6 +191,14 @@
<font weight="400" style="normal">NotoSansKannadaUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansKannadaUI-Bold.ttf</font>
</family>
+ <family variant="elegant">
+ <font weight="400" style="normal">NotoSansOriya-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansOriya-Bold.ttf</font>
+ </family>
+ <family variant="compact">
+ <font weight="400" style="normal">NotoSansOriyaUI-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansOriyaUI-Bold.ttf</font>
+ </family>
<family>
<font weight="400" style="normal">NotoSansSinhala-Regular.ttf</font>
<font weight="700" style="normal">NotoSansSinhala-Bold.ttf</font>
@@ -299,9 +307,6 @@
<family>
<font weight="400" style="normal">NotoSansYi-Regular.ttf</font>
</family>
- <family>
- <font weight="400" style="normal">Lohit-Odia.ttf</font>
- </family>
<family lang="zh-Hans">
<font weight="400" style="normal">NotoSansHans-Regular.otf</font>
</family>
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
index 0bd1dbd..ed690de 100644
--- a/keystore/java/android/security/AndroidKeyStore.java
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -466,74 +466,65 @@
throw new KeyStoreException("Unsupported secret key algorithm: " + keyAlgorithmString);
}
- if ((params.getAlgorithm() != null) && (params.getAlgorithm() != keyAlgorithm)) {
- throw new KeyStoreException("Key algorithm mismatch. Key: " + keyAlgorithmString
- + ", parameter spec: "
- + KeyStoreKeyConstraints.Algorithm.toString(params.getAlgorithm()));
- }
-
KeymasterArguments args = new KeymasterArguments();
args.addInt(KeymasterDefs.KM_TAG_ALGORITHM,
KeyStoreKeyConstraints.Algorithm.toKeymaster(keyAlgorithm));
- if (digest != null) {
- // Digest available from JCA key algorithm
- if (params.getDigest() != null) {
- // Digest also specified in parameters -- check that these two match
- if (digest != params.getDigest()) {
- throw new KeyStoreException("Key digest mismatch. Key: " + keyAlgorithmString
+ @KeyStoreKeyConstraints.DigestEnum int digests;
+ if (params.isDigestsSpecified()) {
+ // Digest(s) specified in parameters
+ if (digest != null) {
+ // Digest also specified in the JCA key algorithm name.
+ if ((params.getDigests() & digest) != digest) {
+ throw new KeyStoreException("Key digest mismatch"
+ + ". Key: " + keyAlgorithmString
+ ", parameter spec: "
- + KeyStoreKeyConstraints.Digest.toString(params.getDigest()));
+ + KeyStoreKeyConstraints.Digest.allToString(params.getDigests()));
}
}
+ digests = params.getDigests();
} else {
- // Digest not available from JCA key algorithm
- digest = params.getDigest();
+ // No digest specified in parameters
+ if (digest != null) {
+ // Digest specified in the JCA key algorithm name.
+ digests = digest;
+ } else {
+ digests = 0;
+ }
}
- if (digest != null) {
- args.addInt(KeymasterDefs.KM_TAG_DIGEST,
- KeyStoreKeyConstraints.Digest.toKeymaster(digest));
+ for (int keymasterDigest : KeyStoreKeyConstraints.Digest.allToKeymaster(digests)) {
+ args.addInt(KeymasterDefs.KM_TAG_DIGEST, keymasterDigest);
+ }
+ if (digests != 0) {
+ // TODO: Remove MAC length constraint once Keymaster API no longer requires it.
+ // This code will blow up if mode than one digest is specified.
Integer digestOutputSizeBytes =
KeyStoreKeyConstraints.Digest.getOutputSizeBytes(digest);
if (digestOutputSizeBytes != null) {
- // TODO: Remove MAC length constraint once Keymaster API no longer requires it.
// TODO: Switch to bits instead of bytes, once this is fixed in Keymaster
args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, digestOutputSizeBytes);
}
}
if (keyAlgorithm == KeyStoreKeyConstraints.Algorithm.HMAC) {
- if (digest == null) {
- throw new IllegalStateException("Digest algorithm must be specified for key"
- + " algorithm " + keyAlgorithmString);
+ if (digests == 0) {
+ throw new KeyStoreException("At least one digest algorithm must be specified"
+ + " for key algorithm " + keyAlgorithmString);
}
}
- @KeyStoreKeyConstraints.PurposeEnum int purposes = (params.getPurposes() != null)
- ? params.getPurposes()
- : (KeyStoreKeyConstraints.Purpose.ENCRYPT
- | KeyStoreKeyConstraints.Purpose.DECRYPT
- | KeyStoreKeyConstraints.Purpose.SIGN
- | KeyStoreKeyConstraints.Purpose.VERIFY);
- for (int keymasterPurpose :
- KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) {
+ int purposes = params.getPurposes();
+ for (int keymasterPurpose : KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) {
args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
}
- if (params.getBlockMode() != null) {
- args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE,
- KeyStoreKeyConstraints.BlockMode.toKeymaster(params.getBlockMode()));
+ for (int keymasterBlockMode :
+ KeyStoreKeyConstraints.BlockMode.allToKeymaster(params.getBlockModes())) {
+ args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockMode);
}
- if (params.getPadding() != null) {
- args.addInt(KeymasterDefs.KM_TAG_PADDING,
- KeyStoreKeyConstraints.Padding.toKeymaster(params.getPadding()));
+ for (int keymasterPadding :
+ KeyStoreKeyConstraints.Padding.allToKeymaster(params.getPaddings())) {
+ args.addInt(KeymasterDefs.KM_TAG_PADDING, keymasterPadding);
}
- if (params.getMaxUsesPerBoot() != null) {
- args.addInt(KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT, params.getMaxUsesPerBoot());
- }
- if (params.getMinSecondsBetweenOperations() != null) {
- args.addInt(KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS,
- params.getMinSecondsBetweenOperations());
- }
- if (params.getUserAuthenticators().isEmpty()) {
+ if (params.getUserAuthenticators() == 0) {
args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
} else {
args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
@@ -544,7 +535,7 @@
// TODO: Add the invalidate on fingerprint enrolled constraint once Keymaster supports
// that.
}
- if (params.getUserAuthenticationValidityDurationSeconds() != null) {
+ if (params.getUserAuthenticationValidityDurationSeconds() != -1) {
args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
params.getUserAuthenticationValidityDurationSeconds());
}
diff --git a/keystore/java/android/security/KeyGeneratorSpec.java b/keystore/java/android/security/KeyGeneratorSpec.java
index 7058383..0e490cd 100644
--- a/keystore/java/android/security/KeyGeneratorSpec.java
+++ b/keystore/java/android/security/KeyGeneratorSpec.java
@@ -19,12 +19,8 @@
import android.content.Context;
import android.text.TextUtils;
-import java.security.cert.Certificate;
import java.security.spec.AlgorithmParameterSpec;
-import java.util.Collections;
import java.util.Date;
-import java.util.HashSet;
-import java.util.Set;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
@@ -33,13 +29,13 @@
* {@link AlgorithmParameterSpec} for initializing a {@code KeyGenerator} that works with
* <a href="{@docRoot}training/articles/keystore.html">Android KeyStore facility</a>.
*
- * <p>The Android KeyStore facility is accessed through a {@link KeyGenerator} API
- * using the {@code AndroidKeyStore} provider. The {@code context} passed in may be used to pop up
- * some UI to ask the user to unlock or initialize the Android KeyStore facility.
+ * <p>The Android KeyStore facility is accessed through a {@link KeyGenerator} API using the
+ * {@code AndroidKeyStore} provider. The {@code context} passed in may be used to pop up some UI to
+ * ask the user to unlock or initialize the Android KeyStore facility.
*
* <p>After generation, the {@code keyStoreAlias} is used with the
* {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter)}
- * interface to retrieve the {@link SecretKey} and its associated {@link Certificate} chain.
+ * interface to retrieve the {@link SecretKey}.
*
* @hide
*/
@@ -52,13 +48,11 @@
private final Date mKeyValidityStart;
private final Date mKeyValidityForOriginationEnd;
private final Date mKeyValidityForConsumptionEnd;
- private final @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
- private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
- private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
- private final Integer mMinSecondsBetweenOperations;
- private final Integer mMaxUsesPerBoot;
- private final Set<Integer> mUserAuthenticators;
- private final Integer mUserAuthenticationValidityDurationSeconds;
+ private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
+ private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
+ private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
+ private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
+ private final int mUserAuthenticationValidityDurationSeconds;
private final boolean mInvalidatedOnNewFingerprintEnrolled;
private KeyGeneratorSpec(
@@ -69,20 +63,18 @@
Date keyValidityStart,
Date keyValidityForOriginationEnd,
Date keyValidityForConsumptionEnd,
- @KeyStoreKeyConstraints.PurposeEnum Integer purposes,
- @KeyStoreKeyConstraints.PaddingEnum Integer padding,
- @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode,
- Integer minSecondsBetweenOperations,
- Integer maxUsesPerBoot,
- Set<Integer> userAuthenticators,
- Integer userAuthenticationValidityDurationSeconds,
+ @KeyStoreKeyConstraints.PurposeEnum int purposes,
+ @KeyStoreKeyConstraints.PaddingEnum int paddings,
+ @KeyStoreKeyConstraints.BlockModeEnum int blockModes,
+ @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators,
+ int userAuthenticationValidityDurationSeconds,
boolean invalidatedOnNewFingerprintEnrolled) {
if (context == null) {
throw new IllegalArgumentException("context == null");
} else if (TextUtils.isEmpty(keyStoreAlias)) {
throw new IllegalArgumentException("keyStoreAlias must not be empty");
- } else if ((userAuthenticationValidityDurationSeconds != null)
- && (userAuthenticationValidityDurationSeconds < 0)) {
+ } else if ((userAuthenticationValidityDurationSeconds < 0)
+ && (userAuthenticationValidityDurationSeconds != -1)) {
throw new IllegalArgumentException(
"userAuthenticationValidityDurationSeconds must not be negative");
}
@@ -95,13 +87,9 @@
mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
mPurposes = purposes;
- mPadding = padding;
- mBlockMode = blockMode;
- mMinSecondsBetweenOperations = minSecondsBetweenOperations;
- mMaxUsesPerBoot = maxUsesPerBoot;
- mUserAuthenticators = (userAuthenticators != null)
- ? new HashSet<Integer>(userAuthenticators)
- : Collections.<Integer>emptySet();
+ mPaddings = paddings;
+ mBlockModes = blockModes;
+ mUserAuthenticators = userAuthenticators;
mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
mInvalidatedOnNewFingerprintEnrolled = invalidatedOnNewFingerprintEnrolled;
}
@@ -148,8 +136,6 @@
* Gets the time instant after which the key is no longer valid for decryption and verification.
*
* @return instant or {@code null} if not restricted.
- *
- * @hide
*/
public Date getKeyValidityForConsumptionEnd() {
return mKeyValidityForConsumptionEnd;
@@ -166,78 +152,43 @@
/**
* Gets the set of purposes for which the key can be used.
- *
- * @return set of purposes or {@code null} if the key can be used for any purpose.
*/
- public @KeyStoreKeyConstraints.PurposeEnum Integer getPurposes() {
+ public @KeyStoreKeyConstraints.PurposeEnum int getPurposes() {
return mPurposes;
}
/**
- * Gets the padding scheme to which the key is restricted.
- *
- * @return padding scheme or {@code null} if the padding scheme is not restricted.
+ * Gets the set of padding schemes to which the key is restricted.
*/
- public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() {
- return mPadding;
+ public @KeyStoreKeyConstraints.PaddingEnum int getPaddings() {
+ return mPaddings;
}
/**
- * Gets the block mode to which the key is restricted when used for encryption or decryption.
- *
- * @return block more or {@code null} if block mode is not restricted.
- *
- * @hide
+ * Gets the set of block modes to which the key is restricted.
*/
- public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() {
- return mBlockMode;
+ public @KeyStoreKeyConstraints.BlockModeEnum int getBlockModes() {
+ return mBlockModes;
}
/**
- * Gets the minimum number of seconds that must expire since the most recent use of the key
- * before it can be used again.
+ * Gets the set of user authenticators which protect access to this key. The key can only be
+ * used iff the user has authenticated to at least one of these user authenticators.
*
- * @return number of seconds or {@code null} if there is no restriction on how frequently a key
- * can be used.
- *
- * @hide
+ * @return user authenticators or {@code 0} if the key can be used without user authentication.
*/
- public Integer getMinSecondsBetweenOperations() {
- return mMinSecondsBetweenOperations;
- }
-
- /**
- * Gets the number of times the key can be used without rebooting the device.
- *
- * @return maximum number of times or {@code null} if there is no restriction.
- * @hide
- */
- public Integer getMaxUsesPerBoot() {
- return mMaxUsesPerBoot;
- }
-
- /**
- * Gets the user authenticators which protect access to this key. The key can only be used iff
- * the user has authenticated to at least one of these user authenticators.
- *
- * @return user authenticators or empty set if the key can be used without user authentication.
- *
- * @hide
- */
- public Set<Integer> getUserAuthenticators() {
- return new HashSet<Integer>(mUserAuthenticators);
+ public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getUserAuthenticators() {
+ return mUserAuthenticators;
}
/**
* Gets the duration of time (seconds) for which this key can be used after the user
* successfully authenticates to one of the associated user authenticators.
*
- * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication
+ * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
* is required for every use of the key.
- *
- * @hide
*/
- public Integer getUserAuthenticationValidityDurationSeconds() {
+ public int getUserAuthenticationValidityDurationSeconds() {
return mUserAuthenticationValidityDurationSeconds;
}
@@ -269,13 +220,11 @@
private Date mKeyValidityStart;
private Date mKeyValidityForOriginationEnd;
private Date mKeyValidityForConsumptionEnd;
- private @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
- private @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
- private @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
- private Integer mMinSecondsBetweenOperations;
- private Integer mMaxUsesPerBoot;
- private Set<Integer> mUserAuthenticators;
- private Integer mUserAuthenticationValidityDurationSeconds;
+ private @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
+ private @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
+ private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
+ private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
+ private int mUserAuthenticationValidityDurationSeconds = -1;
private boolean mInvalidatedOnNewFingerprintEnrolled;
/**
@@ -335,8 +284,6 @@
* <b>By default, the key is valid at any instant.
*
* @see #setKeyValidityEnd(Date)
- *
- * @hide
*/
public Builder setKeyValidityStart(Date startDate) {
mKeyValidityStart = startDate;
@@ -351,8 +298,6 @@
* @see #setKeyValidityStart(Date)
* @see #setKeyValidityForConsumptionEnd(Date)
* @see #setKeyValidityForOriginationEnd(Date)
- *
- * @hide
*/
public Builder setKeyValidityEnd(Date endDate) {
setKeyValidityForOriginationEnd(endDate);
@@ -366,8 +311,6 @@
* <b>By default, the key is valid at any instant.
*
* @see #setKeyValidityForConsumptionEnd(Date)
- *
- * @hide
*/
public Builder setKeyValidityForOriginationEnd(Date endDate) {
mKeyValidityForOriginationEnd = endDate;
@@ -381,8 +324,6 @@
* <b>By default, the key is valid at any instant.
*
* @see #setKeyValidityForOriginationEnd(Date)
- *
- * @hide
*/
public Builder setKeyValidityForConsumptionEnd(Date endDate) {
mKeyValidityForConsumptionEnd = endDate;
@@ -390,11 +331,9 @@
}
/**
- * Restricts the purposes for which the key can be used to the provided set of purposes.
+ * Restricts the key to being used only for the provided set of purposes.
*
- * <p>By default, the key can be used for encryption, decryption, signing, and verification.
- *
- * @hide
+ * <p>This restriction must be specified. There is no default.
*/
public Builder setPurposes(@KeyStoreKeyConstraints.PurposeEnum int purposes) {
mPurposes = purposes;
@@ -402,53 +341,24 @@
}
/**
- * Restricts the key to being used only with the provided padding scheme. Attempts to use
+ * Restricts the key to being used only with the provided padding schemes. Attempts to use
* the key with any other padding will be rejected.
*
* <p>This restriction must be specified for keys which are used for encryption/decryption.
- *
- * @hide
*/
- public Builder setPadding(@KeyStoreKeyConstraints.PaddingEnum int padding) {
- mPadding = padding;
+ public Builder setPaddings(@KeyStoreKeyConstraints.PaddingEnum int paddings) {
+ mPaddings = paddings;
return this;
}
/**
- * Restricts the key to being used only with the provided block mode when encrypting or
- * decrypting. Attempts to use the key with any other block modes will be rejected.
+ * Restricts the key to being used only with the provided block modes. Attempts to use the
+ * key with any other block modes will be rejected.
*
* <p>This restriction must be specified for keys which are used for encryption/decryption.
- *
- * @hide
*/
- public Builder setBlockMode(@KeyStoreKeyConstraints.BlockModeEnum int blockMode) {
- mBlockMode = blockMode;
- return this;
- }
-
- /**
- * Sets the minimum number of seconds that must expire since the most recent use of the key
- * before it can be used again.
- *
- * <p>By default, there is no restriction on how frequently a key can be used.
- *
- * @hide
- */
- public Builder setMinSecondsBetweenOperations(int seconds) {
- mMinSecondsBetweenOperations = seconds;
- return this;
- }
-
- /**
- * Sets the maximum number of times a key can be used without rebooting the device.
- *
- * <p>By default, the key can be used for an unlimited number of times.
- *
- * @hide
- */
- public Builder setMaxUsesPerBoot(int count) {
- mMaxUsesPerBoot = count;
+ public Builder setBlockModes(@KeyStoreKeyConstraints.BlockModeEnum int blockModes) {
+ mBlockModes = blockModes;
return this;
}
@@ -462,12 +372,10 @@
* without user authentication.
*
* @see #setUserAuthenticationValidityDurationSeconds(int)
- *
- * @hide
*/
- public Builder setUserAuthenticators(Set<Integer> userAuthenticators) {
- mUserAuthenticators =
- (userAuthenticators != null) ? new HashSet<Integer>(userAuthenticators) : null;
+ public Builder setUserAuthenticators(
+ @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators) {
+ mUserAuthenticators = userAuthenticators;
return this;
}
@@ -480,9 +388,7 @@
* @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
* every use of the key.
*
- * @see #setUserAuthenticators(Set)
- *
- * @hide
+ * @see #setUserAuthenticators(int)
*/
public Builder setUserAuthenticationValidityDurationSeconds(int seconds) {
mUserAuthenticationValidityDurationSeconds = seconds;
@@ -511,10 +417,18 @@
* @throws IllegalArgumentException if a required field is missing or violates a constraint.
*/
public KeyGeneratorSpec build() {
- return new KeyGeneratorSpec(mContext, mKeystoreAlias, mFlags, mKeySize,
- mKeyValidityStart, mKeyValidityForOriginationEnd, mKeyValidityForConsumptionEnd,
- mPurposes, mPadding, mBlockMode, mMinSecondsBetweenOperations, mMaxUsesPerBoot,
- mUserAuthenticators, mUserAuthenticationValidityDurationSeconds,
+ return new KeyGeneratorSpec(mContext,
+ mKeystoreAlias,
+ mFlags,
+ mKeySize,
+ mKeyValidityStart,
+ mKeyValidityForOriginationEnd,
+ mKeyValidityForConsumptionEnd,
+ mPurposes,
+ mPaddings,
+ mBlockModes,
+ mUserAuthenticators,
+ mUserAuthenticationValidityDurationSeconds,
mInvalidatedOnNewFingerprintEnrolled);
}
}
diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java
index dd62e9a..52b7097 100644
--- a/keystore/java/android/security/KeyPairGeneratorSpec.java
+++ b/keystore/java/android/security/KeyPairGeneratorSpec.java
@@ -24,10 +24,7 @@
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.spec.AlgorithmParameterSpec;
-import java.util.Collections;
import java.util.Date;
-import java.util.HashSet;
-import java.util.Set;
import javax.security.auth.x500.X500Principal;
@@ -81,21 +78,17 @@
private final Date mKeyValidityForConsumptionEnd;
- private final @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
+ private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
- private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
+ private final @KeyStoreKeyConstraints.DigestEnum int mDigests;
- private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
+ private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
- private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
+ private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
- private final Integer mMinSecondsBetweenOperations;
+ private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
- private final Integer mMaxUsesPerBoot;
-
- private final Set<Integer> mUserAuthenticators;
-
- private final Integer mUserAuthenticationValidityDurationSeconds;
+ private final int mUserAuthenticationValidityDurationSeconds;
private final boolean mInvalidatedOnNewFingerprintEnrolled;
@@ -137,14 +130,12 @@
Date keyValidityStart,
Date keyValidityForOriginationEnd,
Date keyValidityForConsumptionEnd,
- @KeyStoreKeyConstraints.PurposeEnum Integer purposes,
- @KeyStoreKeyConstraints.DigestEnum Integer digest,
- @KeyStoreKeyConstraints.PaddingEnum Integer padding,
- @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode,
- Integer minSecondsBetweenOperations,
- Integer maxUsesPerBoot,
- Set<Integer> userAuthenticators,
- Integer userAuthenticationValidityDurationSeconds,
+ @KeyStoreKeyConstraints.PurposeEnum int purposes,
+ @KeyStoreKeyConstraints.DigestEnum int digests,
+ @KeyStoreKeyConstraints.PaddingEnum int paddings,
+ @KeyStoreKeyConstraints.BlockModeEnum int blockModes,
+ @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators,
+ int userAuthenticationValidityDurationSeconds,
boolean invalidatedOnNewFingerprintEnrolled) {
if (context == null) {
throw new IllegalArgumentException("context == null");
@@ -160,8 +151,8 @@
throw new IllegalArgumentException("endDate == null");
} else if (endDate.before(startDate)) {
throw new IllegalArgumentException("endDate < startDate");
- } else if ((userAuthenticationValidityDurationSeconds != null)
- && (userAuthenticationValidityDurationSeconds < 0)) {
+ } else if ((userAuthenticationValidityDurationSeconds < 0)
+ && (userAuthenticationValidityDurationSeconds != -1)) {
throw new IllegalArgumentException(
"userAuthenticationValidityDurationSeconds must not be negative");
}
@@ -180,14 +171,10 @@
mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
mPurposes = purposes;
- mDigest = digest;
- mPadding = padding;
- mBlockMode = blockMode;
- mMinSecondsBetweenOperations = minSecondsBetweenOperations;
- mMaxUsesPerBoot = maxUsesPerBoot;
- mUserAuthenticators = (userAuthenticators != null)
- ? new HashSet<Integer>(userAuthenticators)
- : Collections.<Integer>emptySet();
+ mDigests = digests;
+ mPaddings = paddings;
+ mBlockModes = blockModes;
+ mUserAuthenticators = userAuthenticators;
mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
mInvalidatedOnNewFingerprintEnrolled = invalidatedOnNewFingerprintEnrolled;
}
@@ -200,8 +187,7 @@
AlgorithmParameterSpec spec, X500Principal subjectDN, BigInteger serialNumber,
Date startDate, Date endDate, int flags) {
this(context, keyStoreAlias, keyType, keySize, spec, subjectDN, serialNumber, startDate,
- endDate, flags, startDate, endDate, endDate, null, null, null, null, null, null,
- null, null, false);
+ endDate, flags, startDate, endDate, endDate, 0, 0, 0, 0, 0, -1, false);
}
/**
@@ -327,90 +313,52 @@
/**
* Gets the set of purposes for which the key can be used.
*
- * @return set of purposes or {@code null} if the key can be used for any purpose.
- *
* @hide
*/
- public @KeyStoreKeyConstraints.PurposeEnum Integer getPurposes() {
+ public @KeyStoreKeyConstraints.PurposeEnum int getPurposes() {
return mPurposes;
}
/**
- * Gets the digest to which the key is restricted.
- *
- * @return digest or {@code null} if the digest is not restricted.
+ * Gets the set of digests to which the key is restricted.
*
* @hide
*/
- public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() {
- return mDigest;
+ public @KeyStoreKeyConstraints.DigestEnum int getDigests() {
+ return mDigests;
}
/**
- * Gets the padding scheme to which the key is restricted.
- *
- * @return padding scheme or {@code null} if the padding scheme is not restricted.
+ * Gets the set of padding schemes to which the key is restricted.
*
* @hide
*/
- public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() {
- return mPadding;
+ public @KeyStoreKeyConstraints.PaddingEnum int getPaddings() {
+ return mPaddings;
}
/**
- * Gets the block mode to which the key is restricted when used for encryption or decryption.
- *
- * @return block more or {@code null} if block mode is not restricted.
+ * Gets the set of block modes to which the key is restricted.
*
* @hide
*/
- public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() {
- return mBlockMode;
+ public @KeyStoreKeyConstraints.BlockModeEnum int getBlockModes() {
+ return mBlockModes;
}
/**
- * Gets the minimum number of seconds that must expire since the most recent use of the private
- * key before it can be used again.
+ * Gets the set of user authenticators which protect access to the private key. The key can only
+ * be used iff the user has authenticated to at least one of these user authenticators.
*
* <p>This restriction applies only to private key operations. Public key operations are not
* restricted.
*
- * @return number of seconds or {@code null} if there is no restriction on how frequently a key
- * can be used.
+ * @return user authenticators or {@code 0} if the key can be used without user authentication.
*
* @hide
*/
- public Integer getMinSecondsBetweenOperations() {
- return mMinSecondsBetweenOperations;
- }
-
- /**
- * Gets the number of times the private key can be used without rebooting the device.
- *
- * <p>This restriction applies only to private key operations. Public key operations are not
- * restricted.
- *
- * @return maximum number of times or {@code null} if there is no restriction.
- *
- * @hide
- */
- public Integer getMaxUsesPerBoot() {
- return mMaxUsesPerBoot;
- }
-
- /**
- * Gets the user authenticators which protect access to the private key. The key can only be
- * used iff the user has authenticated to at least one of these user authenticators.
- *
- * <p>This restriction applies only to private key operations. Public key operations are not
- * restricted.
- *
- * @return user authenticators or empty set if the key can be used without user authentication.
- *
- * @hide
- */
- public Set<Integer> getUserAuthenticators() {
- return new HashSet<Integer>(mUserAuthenticators);
+ public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getUserAuthenticators() {
+ return mUserAuthenticators;
}
/**
@@ -420,12 +368,12 @@
* <p>This restriction applies only to private key operations. Public key operations are not
* restricted.
*
- * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication
+ * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
* is required for every use of the key.
*
* @hide
*/
- public Integer getUserAuthenticationValidityDurationSeconds() {
+ public int getUserAuthenticationValidityDurationSeconds() {
return mUserAuthenticationValidityDurationSeconds;
}
@@ -490,21 +438,17 @@
private Date mKeyValidityForConsumptionEnd;
- private @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
+ private @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
- private @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
+ private @KeyStoreKeyConstraints.DigestEnum int mDigests;
- private @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
+ private @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
- private @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
+ private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
- private Integer mMinSecondsBetweenOperations;
+ private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
- private Integer mMaxUsesPerBoot;
-
- private Set<Integer> mUserAuthenticators;
-
- private Integer mUserAuthenticationValidityDurationSeconds;
+ private int mUserAuthenticationValidityDurationSeconds = -1;
private boolean mInvalidatedOnNewFingerprintEnrolled;
@@ -694,9 +638,9 @@
}
/**
- * Restricts the purposes for which the key can be used to the provided set of purposes.
+ * Restricts the key to being used only for the provided set of purposes.
*
- * <p>By default, the key can be used for encryption, decryption, signing, and verification.
+ * <p>This restriction must be specified. There is no default.
*
* @hide
*/
@@ -706,28 +650,28 @@
}
/**
- * Restricts the key to being used only with the provided digest. Attempts to use the key
+ * Restricts the key to being used only with the provided digests. Attempts to use the key
* with any other digests be rejected.
*
* <p>This restriction must be specified for keys which are used for signing/verification.
*
* @hide
*/
- public Builder setDigest(@KeyStoreKeyConstraints.DigestEnum int digest) {
- mDigest = digest;
+ public Builder setDigests(@KeyStoreKeyConstraints.DigestEnum int digests) {
+ mDigests = digests;
return this;
}
/**
- * Restricts the key to being used only with the provided padding scheme. Attempts to use
+ * Restricts the key to being used only with the provided padding schemes. Attempts to use
* the key with any other padding will be rejected.
*
* <p>This restriction must be specified for keys which are used for encryption/decryption.
*
* @hide
*/
- public Builder setPadding(@KeyStoreKeyConstraints.PaddingEnum int padding) {
- mPadding = padding;
+ public Builder setPaddings(@KeyStoreKeyConstraints.PaddingEnum int paddings) {
+ mPaddings = paddings;
return this;
}
@@ -739,39 +683,8 @@
*
* @hide
*/
- public Builder setBlockMode(@KeyStoreKeyConstraints.BlockModeEnum int blockMode) {
- mBlockMode = blockMode;
- return this;
- }
-
- /**
- * Sets the minimum number of seconds that must expire since the most recent use of the key
- * before it can be used again.
- *
- * <p>By default, there is no restriction on how frequently a key can be used.
- *
- * <p>This restriction applies only to private key operations. Public key operations are not
- * restricted.
- *
- * @hide
- */
- public Builder setMinSecondsBetweenOperations(int seconds) {
- mMinSecondsBetweenOperations = seconds;
- return this;
- }
-
- /**
- * Sets the maximum number of times a key can be used without rebooting the device.
- *
- * <p>By default, the key can be used for an unlimited number of times.
- *
- * <p>This restriction applies only to private key operations. Public key operations are not
- * restricted.
- *
- * @hide
- */
- public Builder setMaxUsesPerBoot(int count) {
- mMaxUsesPerBoot = count;
+ public Builder setBlockModes(@KeyStoreKeyConstraints.BlockModeEnum int blockModes) {
+ mBlockModes = blockModes;
return this;
}
@@ -784,16 +697,16 @@
* <p>This restriction applies only to private key operations. Public key operations are not
* restricted.
*
- * @param userAuthenticators user authenticators or empty list if this key can be accessed
+ * @param userAuthenticators user authenticators or {@code 0} if this key can be accessed
* without user authentication.
*
* @see #setUserAuthenticationValidityDurationSeconds(int)
*
* @hide
*/
- public Builder setUserAuthenticators(Set<Integer> userAuthenticators) {
- mUserAuthenticators =
- (userAuthenticators != null) ? new HashSet<Integer>(userAuthenticators) : null;
+ public Builder setUserAuthenticators(
+ @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators) {
+ mUserAuthenticators = userAuthenticators;
return this;
}
@@ -809,7 +722,7 @@
* @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
* every use of the key.
*
- * @see #setUserAuthenticators(Set)
+ * @see #setUserAuthenticators(int)
*
* @hide
*/
@@ -855,11 +768,9 @@
mKeyValidityForOriginationEnd,
mKeyValidityForConsumptionEnd,
mPurposes,
- mDigest,
- mPadding,
- mBlockMode,
- mMinSecondsBetweenOperations,
- mMaxUsesPerBoot,
+ mDigests,
+ mPaddings,
+ mBlockModes,
mUserAuthenticators,
mUserAuthenticationValidityDurationSeconds,
mInvalidatedOnNewFingerprintEnrolled);
diff --git a/keystore/java/android/security/KeyStoreKeyConstraints.java b/keystore/java/android/security/KeyStoreKeyConstraints.java
index 75034d1..7137a9a 100644
--- a/keystore/java/android/security/KeyStoreKeyConstraints.java
+++ b/keystore/java/android/security/KeyStoreKeyConstraints.java
@@ -21,12 +21,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
import java.util.Locale;
-import java.util.Set;
/**
* Constraints for {@code AndroidKeyStore} keys.
@@ -37,7 +33,8 @@
private KeyStoreKeyConstraints() {}
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag=true, value={Purpose.ENCRYPT, Purpose.DECRYPT, Purpose.SIGN, Purpose.VERIFY})
+ @IntDef(flag = true,
+ value = {Purpose.ENCRYPT, Purpose.DECRYPT, Purpose.SIGN, Purpose.VERIFY})
public @interface PurposeEnum {}
/**
@@ -67,11 +64,6 @@
public static final int VERIFY = 1 << 3;
/**
- * Number of flags defined above. Needs to be kept in sync with the flags above.
- */
- private static final int VALUE_COUNT = 4;
-
- /**
* @hide
*/
public static int toKeymaster(@PurposeEnum int purpose) {
@@ -110,22 +102,12 @@
/**
* @hide
*/
- public static int[] allToKeymaster(int purposes) {
- int[] result = new int[VALUE_COUNT];
- int resultCount = 0;
- int purpose = 1;
- for (int i = 0; i < 32; i++) {
- if ((purposes & 1) != 0) {
- result[resultCount] = toKeymaster(purpose);
- resultCount++;
- }
- purposes >>>= 1;
- purpose <<= 1;
- if (purposes == 0) {
- break;
- }
+ public static int[] allToKeymaster(@PurposeEnum int purposes) {
+ int[] result = getSetFlags(purposes);
+ for (int i = 0; i < result.length; i++) {
+ result[i] = toKeymaster(result[i]);
}
- return Arrays.copyOf(result, resultCount);
+ return result;
}
/**
@@ -244,7 +226,8 @@
}
@Retention(RetentionPolicy.SOURCE)
- @IntDef({Padding.NONE, Padding.ZERO, Padding.PKCS7})
+ @IntDef(flag = true,
+ value = {Padding.NONE, Padding.PKCS7})
public @interface PaddingEnum {}
/**
@@ -256,17 +239,12 @@
/**
* No padding.
*/
- public static final int NONE = 0;
-
- /**
- * Pad with zeros.
- */
- public static final int ZERO = 1;
+ public static final int NONE = 1 << 0;
/**
* PKCS#7 padding.
*/
- public static final int PKCS7 = 2;
+ public static final int PKCS7 = 1 << 1;
/**
* @hide
@@ -275,8 +253,6 @@
switch (padding) {
case NONE:
return KeymasterDefs.KM_PAD_NONE;
- case ZERO:
- return KeymasterDefs.KM_PAD_ZERO;
case PKCS7:
return KeymasterDefs.KM_PAD_PKCS7;
default:
@@ -291,8 +267,6 @@
switch (padding) {
case KeymasterDefs.KM_PAD_NONE:
return NONE;
- case KeymasterDefs.KM_PAD_ZERO:
- return ZERO;
case KeymasterDefs.KM_PAD_PKCS7:
return PKCS7;
default:
@@ -307,8 +281,6 @@
switch (padding) {
case NONE:
return "NONE";
- case ZERO:
- return "ZERO";
case PKCS7:
return "PKCS#7";
default:
@@ -329,10 +301,33 @@
throw new IllegalArgumentException("Unknown padding: " + padding);
}
}
+
+ /**
+ * @hide
+ */
+ public static int[] allToKeymaster(@PaddingEnum int paddings) {
+ int[] result = getSetFlags(paddings);
+ for (int i = 0; i < result.length; i++) {
+ result[i] = toKeymaster(result[i]);
+ }
+ return result;
+ }
+
+ /**
+ * @hide
+ */
+ public static @PaddingEnum int allFromKeymaster(Collection<Integer> paddings) {
+ @PaddingEnum int result = 0;
+ for (int keymasterPadding : paddings) {
+ result |= fromKeymaster(keymasterPadding);
+ }
+ return result;
+ }
}
@Retention(RetentionPolicy.SOURCE)
- @IntDef({Digest.NONE, Digest.SHA256})
+ @IntDef(flag = true,
+ value = {Digest.NONE, Digest.SHA256})
public @interface DigestEnum {}
/**
@@ -345,12 +340,12 @@
/**
* No digest: sign/authenticate the raw message.
*/
- public static final int NONE = 0;
+ public static final int NONE = 1 << 0;
/**
* SHA-256 digest.
*/
- public static final int SHA256 = 1;
+ public static final int SHA256 = 1 << 1;
/**
* @hide
@@ -369,6 +364,18 @@
/**
* @hide
*/
+ public static String[] allToString(@DigestEnum int digests) {
+ int[] values = getSetFlags(digests);
+ String[] result = new String[values.length];
+ for (int i = 0; i < values.length; i++) {
+ result[i] = toString(values[i]);
+ }
+ return result;
+ }
+
+ /**
+ * @hide
+ */
public static int toKeymaster(@DigestEnum int digest) {
switch (digest) {
case NONE:
@@ -397,6 +404,28 @@
/**
* @hide
*/
+ public static int[] allToKeymaster(@DigestEnum int digests) {
+ int[] result = getSetFlags(digests);
+ for (int i = 0; i < result.length; i++) {
+ result[i] = toKeymaster(result[i]);
+ }
+ return result;
+ }
+
+ /**
+ * @hide
+ */
+ public static @DigestEnum int allFromKeymaster(Collection<Integer> digests) {
+ @DigestEnum int result = 0;
+ for (int keymasterDigest : digests) {
+ result |= fromKeymaster(keymasterDigest);
+ }
+ return result;
+ }
+
+ /**
+ * @hide
+ */
public static @DigestEnum Integer fromJCASecretKeyAlgorithm(String algorithm) {
String algorithmLower = algorithm.toLowerCase(Locale.US);
if (algorithmLower.startsWith("hmac")) {
@@ -441,7 +470,8 @@
}
@Retention(RetentionPolicy.SOURCE)
- @IntDef({BlockMode.ECB, BlockMode.CBC, BlockMode.CTR})
+ @IntDef(flag = true,
+ value = {BlockMode.ECB, BlockMode.CBC, BlockMode.CTR})
public @interface BlockModeEnum {}
/**
@@ -451,13 +481,13 @@
private BlockMode() {}
/** Electronic Codebook (ECB) block mode. */
- public static final int ECB = 0;
+ public static final int ECB = 1 << 0;
/** Cipher Block Chaining (CBC) block mode. */
- public static final int CBC = 1;
+ public static final int CBC = 1 << 1;
/** Counter (CTR) block mode. */
- public static final int CTR = 2;
+ public static final int CTR = 1 << 2;
/**
* @hide
@@ -494,6 +524,28 @@
/**
* @hide
*/
+ public static int[] allToKeymaster(@BlockModeEnum int modes) {
+ int[] result = getSetFlags(modes);
+ for (int i = 0; i < result.length; i++) {
+ result[i] = toKeymaster(result[i]);
+ }
+ return result;
+ }
+
+ /**
+ * @hide
+ */
+ public static @BlockModeEnum int allFromKeymaster(Collection<Integer> modes) {
+ @BlockModeEnum int result = 0;
+ for (int keymasterMode : modes) {
+ result |= fromKeymaster(keymasterMode);
+ }
+ return result;
+ }
+
+ /**
+ * @hide
+ */
public static String toString(@BlockModeEnum int mode) {
switch (mode) {
case ECB:
@@ -525,7 +577,8 @@
}
@Retention(RetentionPolicy.SOURCE)
- @IntDef({UserAuthenticator.LOCK_SCREEN})
+ @IntDef(flag = true,
+ value = {UserAuthenticator.LOCK_SCREEN})
public @interface UserAuthenticatorEnum {}
/**
@@ -535,7 +588,7 @@
private UserAuthenticator() {}
/** Lock screen. */
- public static final int LOCK_SCREEN = 1;
+ public static final int LOCK_SCREEN = 1 << 0;
/** Fingerprint reader/sensor. */
public static final int FINGERPRINT_READER = 1 << 1;
@@ -546,9 +599,9 @@
public static int toKeymaster(@UserAuthenticatorEnum int userAuthenticator) {
switch (userAuthenticator) {
case LOCK_SCREEN:
- return LOCK_SCREEN;
+ return KeymasterDefs.HW_AUTH_PASSWORD;
case FINGERPRINT_READER:
- return FINGERPRINT_READER;
+ return KeymasterDefs.HW_AUTH_FINGERPRINT;
default:
throw new IllegalArgumentException(
"Unknown user authenticator: " + userAuthenticator);
@@ -560,7 +613,7 @@
*/
public static @UserAuthenticatorEnum int fromKeymaster(int userAuthenticator) {
switch (userAuthenticator) {
- case LOCK_SCREEN:
+ case KeymasterDefs.HW_AUTH_PASSWORD:
return LOCK_SCREEN;
case FINGERPRINT_READER:
return FINGERPRINT_READER;
@@ -573,10 +626,15 @@
/**
* @hide
*/
- public static int allToKeymaster(Set<Integer> userAuthenticators) {
+ public static int allToKeymaster(@UserAuthenticatorEnum int userAuthenticators) {
int result = 0;
- for (@UserAuthenticatorEnum int userAuthenticator : userAuthenticators) {
- result |= toKeymaster(userAuthenticator);
+ int userAuthenticator = 1;
+ while (userAuthenticators != 0) {
+ if ((userAuthenticators & 1) != 0) {
+ result |= toKeymaster(userAuthenticator);
+ }
+ userAuthenticators >>>= 1;
+ userAuthenticator <<= 1;
}
return result;
}
@@ -584,20 +642,17 @@
/**
* @hide
*/
- public static Set<Integer> allFromKeymaster(int userAuthenticators) {
+ public static @UserAuthenticatorEnum int allFromKeymaster(int userAuthenticators) {
+ @UserAuthenticatorEnum int result = 0;
int userAuthenticator = 1;
- Set<Integer> result = null;
while (userAuthenticators != 0) {
if ((userAuthenticators & 1) != 0) {
- if (result == null) {
- result = new HashSet<Integer>();
- }
- result.add(fromKeymaster(userAuthenticator));
+ result |= fromKeymaster(userAuthenticator);
}
userAuthenticators >>>= 1;
userAuthenticator <<= 1;
}
- return (result != null) ? result : Collections.<Integer>emptySet();
+ return result;
}
/**
@@ -615,4 +670,38 @@
}
}
}
+
+ private static final int[] EMPTY_INT_ARRAY = new int[0];
+
+ private static int[] getSetFlags(int flags) {
+ if (flags == 0) {
+ return EMPTY_INT_ARRAY;
+ }
+ int result[] = new int[getSetBitCount(flags)];
+ int resultOffset = 0;
+ int flag = 1;
+ while (flags != 0) {
+ if ((flags & 1) != 0) {
+ result[resultOffset] = flag;
+ resultOffset++;
+ }
+ flags >>>= 1;
+ flag <<= 1;
+ }
+ return result;
+ }
+
+ private static int getSetBitCount(int value) {
+ if (value == 0) {
+ return 0;
+ }
+ int result = 0;
+ while (value != 0) {
+ if ((value & 1) != 0) {
+ result++;
+ }
+ value >>>= 1;
+ }
+ return result;
+ }
}
diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
index 48b6d06..abce32d 100644
--- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
@@ -109,32 +109,20 @@
}
int keySizeBits = (spec.getKeySize() != null) ? spec.getKeySize() : mDefaultKeySizeBits;
args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keySizeBits);
- @KeyStoreKeyConstraints.PurposeEnum int purposes = (spec.getPurposes() != null)
- ? spec.getPurposes()
- : (KeyStoreKeyConstraints.Purpose.ENCRYPT
- | KeyStoreKeyConstraints.Purpose.DECRYPT
- | KeyStoreKeyConstraints.Purpose.SIGN
- | KeyStoreKeyConstraints.Purpose.VERIFY);
+ int purposes = spec.getPurposes();
for (int keymasterPurpose :
KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) {
args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
}
- if (spec.getBlockMode() != null) {
- args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE,
- KeyStoreKeyConstraints.BlockMode.toKeymaster(spec.getBlockMode()));
+ for (int keymasterBlockMode :
+ KeyStoreKeyConstraints.BlockMode.allToKeymaster(spec.getBlockModes())) {
+ args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockMode);
}
- if (spec.getPadding() != null) {
- args.addInt(KeymasterDefs.KM_TAG_PADDING,
- KeyStoreKeyConstraints.Padding.toKeymaster(spec.getPadding()));
+ for (int keymasterPadding :
+ KeyStoreKeyConstraints.Padding.allToKeymaster(spec.getPaddings())) {
+ args.addInt(KeymasterDefs.KM_TAG_PADDING, keymasterPadding);
}
- if (spec.getMaxUsesPerBoot() != null) {
- args.addInt(KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT, spec.getMaxUsesPerBoot());
- }
- if (spec.getMinSecondsBetweenOperations() != null) {
- args.addInt(KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS,
- spec.getMinSecondsBetweenOperations());
- }
- if (spec.getUserAuthenticators().isEmpty()) {
+ if (spec.getUserAuthenticators() == 0) {
args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
} else {
args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
@@ -145,7 +133,7 @@
// TODO: Add the invalidate on fingerprint enrolled constraint once Keymaster supports
// that.
}
- if (spec.getUserAuthenticationValidityDurationSeconds() != null) {
+ if (spec.getUserAuthenticationValidityDurationSeconds() != -1) {
args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
spec.getUserAuthenticationValidityDurationSeconds());
}
diff --git a/keystore/java/android/security/KeyStoreKeySpec.java b/keystore/java/android/security/KeyStoreKeySpec.java
index e5e5acc..256d9b3 100644
--- a/keystore/java/android/security/KeyStoreKeySpec.java
+++ b/keystore/java/android/security/KeyStoreKeySpec.java
@@ -17,10 +17,7 @@
package android.security;
import java.security.spec.KeySpec;
-import java.util.Collections;
import java.util.Date;
-import java.util.HashSet;
-import java.util.Set;
/**
* Information about a key from the <a href="{@docRoot}training/articles/keystore.html">Android
@@ -37,34 +34,31 @@
private final Date mKeyValidityForConsumptionEnd;
private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
private final @KeyStoreKeyConstraints.AlgorithmEnum int mAlgorithm;
- private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
- private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
- private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
- private final Integer mMinSecondsBetweenOperations;
- private final Integer mMaxUsesPerBoot;
- private final Set<Integer> mUserAuthenticators;
- private final Set<Integer> mTeeBackedUserAuthenticators;
- private final Integer mUserAuthenticationValidityDurationSeconds;
+ private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
+ private final @KeyStoreKeyConstraints.DigestEnum int mDigests;
+ private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
+ private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
+ private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mTeeEnforcedUserAuthenticators;
+ private final int mUserAuthenticationValidityDurationSeconds;
private final boolean mInvalidatedOnNewFingerprintEnrolled;
-
/**
* @hide
*/
KeyStoreKeySpec(String keystoreKeyAlias,
@KeyStoreKeyCharacteristics.OriginEnum int origin,
- int keySize, Date keyValidityStart, Date keyValidityForOriginationEnd,
+ int keySize,
+ Date keyValidityStart,
+ Date keyValidityForOriginationEnd,
Date keyValidityForConsumptionEnd,
@KeyStoreKeyConstraints.PurposeEnum int purposes,
@KeyStoreKeyConstraints.AlgorithmEnum int algorithm,
- @KeyStoreKeyConstraints.PaddingEnum Integer padding,
- @KeyStoreKeyConstraints.DigestEnum Integer digest,
- @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode,
- Integer minSecondsBetweenOperations,
- Integer maxUsesPerBoot,
- Set<Integer> userAuthenticators,
- Set<Integer> teeBackedUserAuthenticators,
- Integer userAuthenticationValidityDurationSeconds,
+ @KeyStoreKeyConstraints.PaddingEnum int paddings,
+ @KeyStoreKeyConstraints.DigestEnum int digests,
+ @KeyStoreKeyConstraints.BlockModeEnum int blockModes,
+ @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators,
+ @KeyStoreKeyConstraints.UserAuthenticatorEnum int teeEnforcedUserAuthenticators,
+ int userAuthenticationValidityDurationSeconds,
boolean invalidatedOnNewFingerprintEnrolled) {
mKeystoreAlias = keystoreKeyAlias;
mOrigin = origin;
@@ -74,17 +68,11 @@
mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
mPurposes = purposes;
mAlgorithm = algorithm;
- mPadding = padding;
- mDigest = digest;
- mBlockMode = blockMode;
- mMinSecondsBetweenOperations = minSecondsBetweenOperations;
- mMaxUsesPerBoot = maxUsesPerBoot;
- mUserAuthenticators = (userAuthenticators != null)
- ? new HashSet<Integer>(userAuthenticators)
- : Collections.<Integer>emptySet();
- mTeeBackedUserAuthenticators = (teeBackedUserAuthenticators != null)
- ? new HashSet<Integer>(teeBackedUserAuthenticators)
- : Collections.<Integer>emptySet();
+ mPaddings = paddings;
+ mDigests = digests;
+ mBlockModes = blockModes;
+ mUserAuthenticators = userAuthenticators;
+ mTeeEnforcedUserAuthenticators = teeEnforcedUserAuthenticators;
mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
mInvalidatedOnNewFingerprintEnrolled = invalidatedOnNewFingerprintEnrolled;
}
@@ -104,7 +92,7 @@
}
/**
- * Gets the key's size in bits.
+ * Gets the size of the key in bits.
*/
public int getKeySize() {
return mKeySize;
@@ -152,78 +140,53 @@
}
/**
- * Gets the only block mode with which the key can be used.
- *
- * @return block mode or {@code null} if the block mode is not restricted.
+ * Gets the set of block modes with which the key can be used.
*/
- public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() {
- return mBlockMode;
+ public @KeyStoreKeyConstraints.BlockModeEnum int getBlockModes() {
+ return mBlockModes;
}
/**
- * Gets the only padding mode with which the key can be used.
- *
- * @return padding mode or {@code null} if the padding mode is not restricted.
+ * Gets the set of padding modes with which the key can be used.
*/
- public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() {
- return mPadding;
+ public @KeyStoreKeyConstraints.PaddingEnum int getPaddings() {
+ return mPaddings;
}
/**
- * Gets the only digest algorithm with which the key can be used.
- *
- * @return digest algorithm or {@code null} if the digest algorithm is not restricted.
+ * Gets the set of digest algorithms with which the key can be used.
*/
- public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() {
- return mDigest;
+ public @KeyStoreKeyConstraints.DigestEnum int getDigests() {
+ return mDigests;
}
/**
- * Gets the minimum number of seconds that must expire since the most recent use of the key
- * before it can be used again.
+ * Gets the set of user authenticators which protect access to the key. The key can only be used
+ * iff the user has authenticated to at least one of these user authenticators.
*
- * @return number of seconds or {@code null} if there is no restriction on how frequently a key
- * can be used.
+ * @return user authenticators or {@code 0} if the key can be used without user authentication.
*/
- public Integer getMinSecondsBetweenOperations() {
- return mMinSecondsBetweenOperations;
+ public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getUserAuthenticators() {
+ return mUserAuthenticators;
}
/**
- * Gets the number of times the key can be used without rebooting the device.
- *
- * @return maximum number of times or {@code null} if there is no restriction.
+ * Gets the set of user authenticators for which the TEE enforces access restrictions for this
+ * key. This is a subset of the user authentications returned by
+ * {@link #getUserAuthenticators()}.
*/
- public Integer getMaxUsesPerBoot() {
- return mMaxUsesPerBoot;
- }
-
- /**
- * Gets the user authenticators which protect access to the key. The key can only be used iff
- * the user has authenticated to at least one of these user authenticators.
- *
- * @return user authenticators or empty set if the key can be used without user authentication.
- */
- public Set<Integer> getUserAuthenticators() {
- return new HashSet<Integer>(mUserAuthenticators);
- }
-
- /**
- * Gets the TEE-backed user authenticators which protect access to the key. This is a subset of
- * the user authentications returned by {@link #getUserAuthenticators()}.
- */
- public Set<Integer> getTeeBackedUserAuthenticators() {
- return new HashSet<Integer>(mTeeBackedUserAuthenticators);
+ public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getTeeEnforcedUserAuthenticators() {
+ return mTeeEnforcedUserAuthenticators;
}
/**
* Gets the duration of time (seconds) for which the key can be used after the user
* successfully authenticates to one of the associated user authenticators.
*
- * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication
+ * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
* is required for every use of the key.
*/
- public Integer getUserAuthenticationValidityDurationSeconds() {
+ public int getUserAuthenticationValidityDurationSeconds() {
return mUserAuthenticationValidityDurationSeconds;
}
diff --git a/keystore/java/android/security/KeyStoreParameter.java b/keystore/java/android/security/KeyStoreParameter.java
index 88bd6b4..0b2f9b6 100644
--- a/keystore/java/android/security/KeyStoreParameter.java
+++ b/keystore/java/android/security/KeyStoreParameter.java
@@ -18,12 +18,10 @@
import android.content.Context;
+import java.security.Key;
import java.security.KeyPairGenerator;
import java.security.KeyStore.ProtectionParameter;
-import java.util.Collections;
import java.util.Date;
-import java.util.HashSet;
-import java.util.Set;
/**
* This provides the optional parameters that can be specified for
@@ -50,33 +48,27 @@
private final Date mKeyValidityStart;
private final Date mKeyValidityForOriginationEnd;
private final Date mKeyValidityForConsumptionEnd;
- private final @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
- private final @KeyStoreKeyConstraints.AlgorithmEnum Integer mAlgorithm;
- private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
- private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
- private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
- private final Integer mMinSecondsBetweenOperations;
- private final Integer mMaxUsesPerBoot;
- private final Set<Integer> mUserAuthenticators;
- private final Integer mUserAuthenticationValidityDurationSeconds;
+ private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
+ private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
+ private final @KeyStoreKeyConstraints.DigestEnum Integer mDigests;
+ private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
+ private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
+ private final int mUserAuthenticationValidityDurationSeconds;
private final boolean mInvalidatedOnNewFingerprintEnrolled;
private KeyStoreParameter(int flags,
Date keyValidityStart,
Date keyValidityForOriginationEnd,
Date keyValidityForConsumptionEnd,
- @KeyStoreKeyConstraints.PurposeEnum Integer purposes,
- @KeyStoreKeyConstraints.AlgorithmEnum Integer algorithm,
- @KeyStoreKeyConstraints.PaddingEnum Integer padding,
- @KeyStoreKeyConstraints.DigestEnum Integer digest,
- @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode,
- Integer minSecondsBetweenOperations,
- Integer maxUsesPerBoot,
- Set<Integer> userAuthenticators,
- Integer userAuthenticationValidityDurationSeconds,
+ @KeyStoreKeyConstraints.PurposeEnum int purposes,
+ @KeyStoreKeyConstraints.PaddingEnum int paddings,
+ @KeyStoreKeyConstraints.DigestEnum Integer digests,
+ @KeyStoreKeyConstraints.BlockModeEnum int blockModes,
+ @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators,
+ int userAuthenticationValidityDurationSeconds,
boolean invalidatedOnNewFingerprintEnrolled) {
- if ((userAuthenticationValidityDurationSeconds != null)
- && (userAuthenticationValidityDurationSeconds < 0)) {
+ if ((userAuthenticationValidityDurationSeconds < 0)
+ && (userAuthenticationValidityDurationSeconds != -1)) {
throw new IllegalArgumentException(
"userAuthenticationValidityDurationSeconds must not be negative");
}
@@ -86,15 +78,10 @@
mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
mPurposes = purposes;
- mAlgorithm = algorithm;
- mPadding = padding;
- mDigest = digest;
- mBlockMode = blockMode;
- mMinSecondsBetweenOperations = minSecondsBetweenOperations;
- mMaxUsesPerBoot = maxUsesPerBoot;
- mUserAuthenticators = (userAuthenticators != null)
- ? new HashSet<Integer>(userAuthenticators)
- : Collections.<Integer>emptySet();
+ mPaddings = paddings;
+ mDigests = digests;
+ mBlockModes = blockModes;
+ mUserAuthenticators = userAuthenticators;
mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
mInvalidatedOnNewFingerprintEnrolled = invalidatedOnNewFingerprintEnrolled;
}
@@ -147,105 +134,81 @@
}
/**
- * Gets the set of purposes for which the key can be used to the provided set of purposes.
- *
- * @return set of purposes or {@code null} if the key can be used for any purpose.
+ * Gets the set of purposes for which the key can be used.
*
* @hide
*/
- public @KeyStoreKeyConstraints.PurposeEnum Integer getPurposes() {
+ public @KeyStoreKeyConstraints.PurposeEnum int getPurposes() {
return mPurposes;
}
/**
- * Gets the algorithm to which the key is restricted.
+ * Gets the set of padding schemes to which the key is restricted.
*
- * @return algorithm or {@code null} if it's not restricted.
* @hide
*/
- public @KeyStoreKeyConstraints.AlgorithmEnum Integer getAlgorithm() {
- return mAlgorithm;
+ public @KeyStoreKeyConstraints.PaddingEnum int getPaddings() {
+ return mPaddings;
}
/**
- * Gets the padding scheme to which the key is restricted.
+ * Gets the set of digests to which the key is restricted.
*
- * @return padding scheme or {@code null} if the padding scheme is not restricted.
+ * @throws IllegalStateException if this restriction has not been specified.
+ *
+ * @see #isDigestsSpecified()
*
* @hide
*/
- public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() {
- return mPadding;
+ public @KeyStoreKeyConstraints.DigestEnum int getDigests() {
+ if (mDigests == null) {
+ throw new IllegalStateException("Digests not specified");
+ }
+ return mDigests;
}
/**
- * Gets the digest to which the key is restricted when generating signatures or Message
- * Authentication Codes (MACs).
+ * Returns {@code true} if digest restrictions have been specified.
*
- * @return digest or {@code null} if the digest is not restricted.
+ * @see #getDigests()
*
* @hide
*/
- public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() {
- return mDigest;
+ public boolean isDigestsSpecified() {
+ return mDigests != null;
}
/**
- * Gets the block mode to which the key is restricted when used for encryption or decryption.
- *
- * @return block more or {@code null} if block mode is not restricted.
+ * Gets the set of block modes to which the key is restricted.
*
* @hide
*/
- public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() {
- return mBlockMode;
+ public @KeyStoreKeyConstraints.BlockModeEnum int getBlockModes() {
+ return mBlockModes;
}
/**
- * Gets the minimum number of seconds that must expire since the most recent use of the key
- * before it can be used again.
+ * Gets the set of user authenticators which protect access to this key. The key can only be
+ * used iff the user has authenticated to at least one of these user authenticators.
*
- * @return number of seconds or {@code null} if there is no restriction on how frequently a key
- * can be used.
+ * @return user authenticators or {@code 0} if the key can be used without user authentication.
*
* @hide
*/
- public Integer getMinSecondsBetweenOperations() {
- return mMinSecondsBetweenOperations;
- }
-
- /**
- * Gets the number of times the key can be used without rebooting the device.
- *
- * @return maximum number of times or {@code null} if there is no restriction.
- * @hide
- */
- public Integer getMaxUsesPerBoot() {
- return mMaxUsesPerBoot;
- }
-
- /**
- * Gets the user authenticators which protect access to this key. The key can only be used iff
- * the user has authenticated to at least one of these user authenticators.
- *
- * @return user authenticators or empty set if the key can be used without user authentication.
- *
- * @hide
- */
- public Set<Integer> getUserAuthenticators() {
- return new HashSet<Integer>(mUserAuthenticators);
+ public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getUserAuthenticators() {
+ return mUserAuthenticators;
}
/**
* Gets the duration of time (seconds) for which this key can be used after the user
* successfully authenticates to one of the associated user authenticators.
*
- * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication
+ * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
* is required for every use of the key.
*
* @hide
*/
- public Integer getUserAuthenticationValidityDurationSeconds() {
+ public int getUserAuthenticationValidityDurationSeconds() {
return mUserAuthenticationValidityDurationSeconds;
}
@@ -284,15 +247,12 @@
private Date mKeyValidityStart;
private Date mKeyValidityForOriginationEnd;
private Date mKeyValidityForConsumptionEnd;
- private @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
- private @KeyStoreKeyConstraints.AlgorithmEnum Integer mAlgorithm;
- private @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
- private @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
- private @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
- private Integer mMinSecondsBetweenOperations;
- private Integer mMaxUsesPerBoot;
- private Set<Integer> mUserAuthenticators;
- private Integer mUserAuthenticationValidityDurationSeconds;
+ private @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
+ private @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
+ private @KeyStoreKeyConstraints.DigestEnum Integer mDigests;
+ private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
+ private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
+ private int mUserAuthenticationValidityDurationSeconds = -1;
private boolean mInvalidatedOnNewFingerprintEnrolled;
/**
@@ -385,9 +345,9 @@
}
/**
- * Restricts the purposes for which the key can be used to the provided set of purposes.
+ * Restricts the key to being used only for the provided set of purposes.
*
- * <p>By default, the key can be used for encryption, decryption, signing, and verification.
+ * <p>This restriction must be specified. There is no default.
*
* @hide
*/
@@ -397,84 +357,43 @@
}
/**
- * Sets the algorithm of the key.
- *
- * <p>The algorithm of symmetric keys can be deduced from the key itself. Thus, explicitly
- * specifying the algorithm of symmetric keys using this method is not necessary.
- *
- * @hide
- */
- public Builder setAlgorithm(@KeyStoreKeyConstraints.AlgorithmEnum int algorithm) {
- mAlgorithm = algorithm;
- return this;
- }
-
- /**
- * Restricts the key to being used only with the provided padding scheme. Attempts to use
+ * Restricts the key to being used only with the provided padding schemes. Attempts to use
* the key with any other padding will be rejected.
*
* <p>This restriction must be specified for keys which are used for encryption/decryption.
*
* @hide
*/
- public Builder setPadding(@KeyStoreKeyConstraints.PaddingEnum int padding) {
- mPadding = padding;
+ public Builder setPaddings(@KeyStoreKeyConstraints.PaddingEnum int paddings) {
+ mPaddings = paddings;
return this;
}
/**
- * Restricts the key to being used only with the provided digest when generating signatures
- * or Message Authentication Codes (MACs). Attempts to use the key with any other digest
- * will be rejected.
+ * Restricts the key to being used only with the provided digests when generating signatures
+ * or HMACs. Attempts to use the key with any other digest will be rejected.
*
- * <p>For MAC keys, the default is to restrict to the digest specified in the key algorithm
- * name. For asymmetric signing keys this constraint must be specified because there is no
- * default.
- *
- * @see java.security.Key#getAlgorithm()
+ * <p>For HMAC keys, the default is to restrict to the digest specified in
+ * {@link Key#getAlgorithm()}. For asymmetric signing keys this constraint must be specified
+ * because there is no default.
*
* @hide
*/
- public Builder setDigest(@KeyStoreKeyConstraints.DigestEnum int digest) {
- mDigest = digest;
+ public Builder setDigests(@KeyStoreKeyConstraints.DigestEnum int digests) {
+ mDigests = digests;
return this;
}
/**
- * Restricts the key to being used only with the provided block mode when encrypting or
- * decrypting. Attempts to use the key with any other block modes will be rejected.
+ * Restricts the key to being used only with the provided block modes. Attempts to use the
+ * key with any other block modes will be rejected.
*
- * <p>This restriction must be specified for keys which are used for encryption/decryption.
+ * <p>This restriction must be specified for symmetric encryption/decryption keys.
*
* @hide
*/
- public Builder setBlockMode(@KeyStoreKeyConstraints.BlockModeEnum int blockMode) {
- mBlockMode = blockMode;
- return this;
- }
-
- /**
- * Sets the minimum number of seconds that must expire since the most recent use of the key
- * before it can be used again.
- *
- * <p>By default, there is no restriction on how frequently a key can be used.
- *
- * @hide
- */
- public Builder setMinSecondsBetweenOperations(int seconds) {
- mMinSecondsBetweenOperations = seconds;
- return this;
- }
-
- /**
- * Sets the maximum number of times a key can be used without rebooting the device.
- *
- * <p>By default, the key can be used for an unlimited number of times.
- *
- * @hide
- */
- public Builder setMaxUsesPerBoot(int count) {
- mMaxUsesPerBoot = count;
+ public Builder setBlockModes(@KeyStoreKeyConstraints.BlockModeEnum int blockModes) {
+ mBlockModes = blockModes;
return this;
}
@@ -484,16 +403,16 @@
*
* <p>By default, the key can be used without user authentication.
*
- * @param userAuthenticators user authenticators or empty list if this key can be accessed
+ * @param userAuthenticators user authenticators or {@code 0} if this key can be accessed
* without user authentication.
*
* @see #setUserAuthenticationValidityDurationSeconds(int)
*
* @hide
*/
- public Builder setUserAuthenticators(Set<Integer> userAuthenticators) {
- mUserAuthenticators =
- (userAuthenticators != null) ? new HashSet<Integer>(userAuthenticators) : null;
+ public Builder setUserAuthenticators(
+ @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators) {
+ mUserAuthenticators = userAuthenticators;
return this;
}
@@ -506,7 +425,7 @@
* @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
* every use of the key.
*
- * @see #setUserAuthenticators(Set)
+ * @see #setUserAuthenticators(int)
*
* @hide
*/
@@ -543,12 +462,9 @@
mKeyValidityForOriginationEnd,
mKeyValidityForConsumptionEnd,
mPurposes,
- mAlgorithm,
- mPadding,
- mDigest,
- mBlockMode,
- mMinSecondsBetweenOperations,
- mMaxUsesPerBoot,
+ mPaddings,
+ mDigests,
+ mBlockModes,
mUserAuthenticators,
mUserAuthenticationValidityDurationSeconds,
mInvalidatedOnNewFingerprintEnrolled);
diff --git a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
index c205d9d..8bf228a 100644
--- a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
@@ -23,7 +23,6 @@
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Date;
-import java.util.Set;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactorySpi;
@@ -75,9 +74,11 @@
int keySize;
@KeyStoreKeyConstraints.PurposeEnum int purposes;
@KeyStoreKeyConstraints.AlgorithmEnum int algorithm;
- @KeyStoreKeyConstraints.PaddingEnum Integer padding;
- @KeyStoreKeyConstraints.DigestEnum Integer digest;
- @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode;
+ @KeyStoreKeyConstraints.PaddingEnum int paddings;
+ @KeyStoreKeyConstraints.DigestEnum int digests;
+ @KeyStoreKeyConstraints.BlockModeEnum int blockModes;
+ @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators;
+ @KeyStoreKeyConstraints.UserAuthenticatorEnum int teeEnforcedUserAuthenticators;
try {
origin = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_ORIGIN);
if (origin == null) {
@@ -97,18 +98,27 @@
throw new InvalidKeySpecException("Key algorithm not available");
}
algorithm = KeyStoreKeyConstraints.Algorithm.fromKeymaster(alg);
- padding = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_PADDING);
- if (padding != null) {
- padding = KeyStoreKeyConstraints.Padding.fromKeymaster(padding);
- }
- digest = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_DIGEST);
- if (digest != null) {
- digest = KeyStoreKeyConstraints.Digest.fromKeymaster(digest);
- }
- blockMode = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_BLOCK_MODE);
- if (blockMode != null) {
- blockMode = KeyStoreKeyConstraints.BlockMode.fromKeymaster(blockMode);
- }
+ paddings = KeyStoreKeyConstraints.Padding.allFromKeymaster(
+ KeymasterUtils.getInts(keyCharacteristics, KeymasterDefs.KM_TAG_PADDING));
+ digests = KeyStoreKeyConstraints.Digest.allFromKeymaster(
+ KeymasterUtils.getInts(keyCharacteristics, KeymasterDefs.KM_TAG_DIGEST));
+ blockModes = KeyStoreKeyConstraints.BlockMode.allFromKeymaster(
+ KeymasterUtils.getInts(keyCharacteristics, KeymasterDefs.KM_TAG_BLOCK_MODE));
+
+ @KeyStoreKeyConstraints.UserAuthenticatorEnum
+ int swEnforcedKeymasterUserAuthenticators =
+ keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
+ @KeyStoreKeyConstraints.UserAuthenticatorEnum
+ int hwEnforcedKeymasterUserAuthenticators =
+ keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
+ @KeyStoreKeyConstraints.UserAuthenticatorEnum
+ int keymasterUserAuthenticators =
+ swEnforcedKeymasterUserAuthenticators | hwEnforcedKeymasterUserAuthenticators;
+ userAuthenticators = KeyStoreKeyConstraints.UserAuthenticator.allFromKeymaster(
+ keymasterUserAuthenticators);
+ teeEnforcedUserAuthenticators =
+ KeyStoreKeyConstraints.UserAuthenticator.allFromKeymaster(
+ hwEnforcedKeymasterUserAuthenticators);
} catch (IllegalArgumentException e) {
throw new InvalidKeySpecException("Unsupported key characteristic", e);
}
@@ -130,17 +140,8 @@
&& (keyValidityForConsumptionEnd.getTime() == Long.MAX_VALUE)) {
keyValidityForConsumptionEnd = null;
}
-
- int swEnforcedUserAuthenticatorIds =
- keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
- int hwEnforcedUserAuthenticatorIds =
- keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
- int userAuthenticatorIds = swEnforcedUserAuthenticatorIds | hwEnforcedUserAuthenticatorIds;
- Set<Integer> userAuthenticators =
- KeyStoreKeyConstraints.UserAuthenticator.allFromKeymaster(userAuthenticatorIds);
- Set<Integer> teeBackedUserAuthenticators =
- KeyStoreKeyConstraints.UserAuthenticator.allFromKeymaster(
- hwEnforcedUserAuthenticatorIds);
+ Integer userAuthenticationValidityDurationSeconds =
+ KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_AUTH_TIMEOUT);
// TODO: Populate the value below from key characteristics once Keymaster is ready.
boolean invalidatedOnNewFingerprintEnrolled = false;
@@ -153,15 +154,13 @@
keyValidityForConsumptionEnd,
purposes,
algorithm,
- padding,
- digest,
- blockMode,
- KeymasterUtils.getInt(keyCharacteristics,
- KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS),
- KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT),
+ paddings,
+ digests,
+ blockModes,
userAuthenticators,
- teeBackedUserAuthenticators,
- KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_AUTH_TIMEOUT),
+ teeEnforcedUserAuthenticators,
+ ((userAuthenticationValidityDurationSeconds != null)
+ ? userAuthenticationValidityDurationSeconds : -1),
invalidatedOnNewFingerprintEnrolled);
}
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 65be9e1..a72faea 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -169,6 +169,10 @@
newData->jankFrameCount += mData->jankFrameCount;
newData->totalFrameCount >>= divider;
newData->totalFrameCount += mData->totalFrameCount;
+ if (newData->statStartTime > mData->statStartTime
+ || newData->statStartTime == 0) {
+ newData->statStartTime = mData->statStartTime;
+ }
freeData();
mData = newData;
@@ -235,6 +239,7 @@
}
void JankTracker::dumpData(const ProfileData* data, int fd) {
+ dprintf(fd, "\nStats since: %lluns", data->statStartTime);
dprintf(fd, "\nTotal frames rendered: %u", data->totalFrameCount);
dprintf(fd, "\nJanky frames: %u (%.2f%%)", data->jankFrameCount,
(float) data->jankFrameCount / (float) data->totalFrameCount * 100.0f);
@@ -252,6 +257,7 @@
mData->frameCounts.fill(0);
mData->totalFrameCount = 0;
mData->jankFrameCount = 0;
+ mData->statStartTime = systemTime(CLOCK_MONOTONIC);
}
uint32_t JankTracker::findPercentile(const ProfileData* data, int percentile) {
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index 4783001..3887e5e 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -44,10 +44,11 @@
struct ProfileData {
std::array<uint32_t, NUM_BUCKETS> jankTypeCounts;
// See comments on kBucket* constants for what this holds
- std::array<uint32_t, 57> frameCounts;
+ std::array<uint32_t, 55> frameCounts;
uint32_t totalFrameCount;
uint32_t jankFrameCount;
+ nsecs_t statStartTime;
};
// TODO: Replace DrawProfiler with this
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 98bfaff..4c5fb40 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1499,7 +1499,9 @@
* @param sizeInShorts the number of shorts to read in audioData after the offset.
* @return the number of shorts that were written or {@link #ERROR_INVALID_OPERATION}
* if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
- * the parameters don't resolve to valid data and indexes.
+ * the parameters don't resolve to valid data and indexes, or
+ * {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
+ * needs to be recreated.
*/
public int write(short[] audioData, int offsetInShorts, int sizeInShorts) {
@@ -1559,7 +1561,9 @@
* queuing as much audio data for playback as possible without blocking.
* @return the number of floats that were written, or {@link #ERROR_INVALID_OPERATION}
* if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
- * the parameters don't resolve to valid data and indexes.
+ * the parameters don't resolve to valid data and indexes, or
+ * {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
+ * needs to be recreated.
*/
public int write(float[] audioData, int offsetInFloats, int sizeInFloats,
@WriteMode int writeMode) {
@@ -1620,7 +1624,9 @@
* <BR>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
* queuing as much audio data for playback as possible without blocking.
* @return 0 or a positive number of bytes that were written, or
- * {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION}
+ * {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION}, or
+ * {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
+ * needs to be recreated.
*/
public int write(ByteBuffer audioData, int sizeInBytes,
@WriteMode int writeMode) {
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index a7f33fa..fd7fca6 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -540,6 +540,8 @@
* or the flags are not set properly
* (e.g. missing {@link #CONFIGURE_FLAG_ENCODE} for an encoder).
* @throws IllegalStateException if not in the Initialized state.
+ * @throws CryptoException upon DRM error.
+ * @throws CodecException upon codec error.
*/
public void configure(
MediaFormat format,
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
old mode 100755
new mode 100644
index 64b4452..dd2368f
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -527,7 +527,11 @@
void onUuidChanged() {
updateProfiles();
ParcelUuid[] uuids = mDevice.getUuids();
+
long timeout = MAX_UUID_DELAY_FOR_AUTO_CONNECT;
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hogp)) {
+ timeout = MAX_HOGP_DELAY_FOR_AUTO_CONNECT;
+ }
if (DEBUG) {
Log.d(TAG, "onUuidChanged: Time since last connect"
@@ -535,14 +539,12 @@
}
/*
- * If a connect was attempted earlier without any UUID, we will do the
- * connect now.
+ * If a connect was attempted earlier without any UUID, we will do the connect now.
+ * Otherwise, allow the connect on UUID change.
*/
- if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hogp)) {
- timeout = MAX_HOGP_DELAY_FOR_AUTO_CONNECT;
- }
if (!mProfiles.isEmpty()
- && (mConnectAttempted + timeout) > SystemClock.elapsedRealtime()) {
+ && ((mConnectAttempted + timeout) > SystemClock.elapsedRealtime()
+ || (mConnectAttempted == 0))) {
connectWithoutResettingTimer(false);
}
dispatchAttributesChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index ff6a45a..64730c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -21,6 +21,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.view.LayoutInflater;
@@ -46,20 +47,17 @@
private final DndDetailAdapter mDetailAdapter;
private boolean mListening;
- private boolean mVisible;
private boolean mShowingDetail;
public DndTile(Host host) {
super(host);
mController = host.getZenModeController();
mDetailAdapter = new DndDetailAdapter();
- mVisible = isVisible(host.getContext());
mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_SET_VISIBLE));
}
public static void setVisible(Context context, boolean visible) {
- context.sendBroadcast(new Intent(DndTile.ACTION_SET_VISIBLE)
- .putExtra(DndTile.EXTRA_VISIBLE, visible));
+ getSharedPrefs(context).edit().putBoolean(PREF_KEY_VISIBLE, visible).commit();
}
public static boolean isVisible(Context context) {
@@ -98,7 +96,7 @@
protected void handleUpdateState(BooleanState state, Object arg) {
final int zen = arg instanceof Integer ? (Integer) arg : mController.getZen();
state.value = zen != Global.ZEN_MODE_OFF;
- state.visible = mVisible;
+ state.visible = isVisible(mContext);
switch (zen) {
case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on);
@@ -145,26 +143,38 @@
mListening = listening;
if (mListening) {
mController.addCallback(mZenCallback);
+ getSharedPrefs(mContext).registerOnSharedPreferenceChangeListener(mPrefListener);
} else {
mController.removeCallback(mZenCallback);
+ getSharedPrefs(mContext).unregisterOnSharedPreferenceChangeListener(mPrefListener);
}
}
+ private static SharedPreferences getSharedPrefs(Context context) {
+ return context.getSharedPreferences(context.getPackageName(), 0);
+ }
+
+ private final OnSharedPreferenceChangeListener mPrefListener
+ = new OnSharedPreferenceChangeListener() {
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ if (PREF_KEY_COMBINED_ICON.equals(key) || PREF_KEY_VISIBLE.equals(key)) {
+ refreshState();
+ }
+ }
+ };
+
private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
public void onZenChanged(int zen) {
refreshState(zen);
}
};
- private static SharedPreferences getSharedPrefs(Context context) {
- return context.getSharedPreferences(context.getPackageName(), 0);
- }
-
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- mVisible = intent.getBooleanExtra(EXTRA_VISIBLE, false);
- getSharedPrefs(mContext).edit().putBoolean(PREF_KEY_VISIBLE, mVisible).commit();
+ final boolean visible = intent.getBooleanExtra(EXTRA_VISIBLE, false);
+ setVisible(mContext, visible);
refreshState();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index 3b8fccc..cab152f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -69,7 +69,7 @@
@Override
public void onAnimationEnd(Animator animation) {
mSwipeAnimator = null;
- setSwipingInProgress(false);
+ mSwipingInProgress = false;
}
};
private Runnable mAnimationEndRunnable = new Runnable() {
@@ -117,14 +117,17 @@
}
public boolean onTouchEvent(MotionEvent event) {
- if (mMotionCancelled && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
+ int action = event.getActionMasked();
+ if (mMotionCancelled && action != MotionEvent.ACTION_DOWN
+ && action != MotionEvent.ACTION_UP
+ && action != MotionEvent.ACTION_CANCEL) {
return false;
}
final float y = event.getY();
final float x = event.getX();
boolean isUp = false;
- switch (event.getActionMasked()) {
+ switch (action) {
case MotionEvent.ACTION_DOWN:
if (mSwipingInProgress) {
cancelAnimation();
@@ -152,7 +155,8 @@
mInitialTouchY = y;
mInitialTouchX = x;
mTranslationOnDown = mTranslation;
- setSwipingInProgress(true);
+ mSwipingInProgress = true;
+ mCallback.onSwipingStarted(w < -mTouchSlop);
}
if (mSwipingInProgress) {
setTranslation(mTranslationOnDown + x - mInitialTouchX, false, false);
@@ -179,13 +183,6 @@
}
}
- private void setSwipingInProgress(boolean inProgress) {
- mSwipingInProgress = inProgress;
- if (inProgress) {
- mCallback.onSwipingStarted();
- }
- }
-
private boolean rightSwipePossible() {
return mRightIcon.getVisibility() == View.VISIBLE;
}
@@ -323,6 +320,9 @@
}
animator.start();
mSwipeAnimator = animator;
+ if (snapBack) {
+ mCallback.onSwipingAborted();
+ }
}
private void startFinishingCircleAnimation(float velocity, Runnable mAnimationEndRunnable) {
@@ -451,7 +451,11 @@
mSwipeAnimator.cancel();
}
setTranslation(0.0f, true, animate);
- setSwipingInProgress(false);
+ mMotionCancelled = true;
+ if (mSwipingInProgress) {
+ mCallback.onSwipingAborted();
+ }
+ mSwipingInProgress = false;
}
public interface Callback {
@@ -470,7 +474,9 @@
float getPageWidth();
- void onSwipingStarted();
+ void onSwipingStarted(boolean isRightwardMotion);
+
+ void onSwipingAborted();
KeyguardAffordanceView getLeftIcon();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index a247c8e..628ae84 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -100,6 +100,7 @@
private final TrustDrawable mTrustDrawable;
private final Interpolator mLinearOutSlowInInterpolator;
private int mLastUnlockIconRes = 0;
+ private boolean mPrewarmSent;
public KeyguardBottomAreaView(Context context) {
this(context, null);
@@ -335,12 +336,47 @@
mLockPatternUtils.requireCredentialEntry(mLockPatternUtils.getCurrentUser());
}
- public void launchCamera() {
+ public void prewarmCamera() {
Intent intent = getCameraIntent();
+ String targetPackage = PreviewInflater.getTargetPackage(mContext, intent,
+ mLockPatternUtils.getCurrentUser());
+ if (targetPackage != null) {
+ Intent prewarm = new Intent(MediaStore.ACTION_STILL_IMAGE_CAMERA_PREWARM);
+ prewarm.setPackage(targetPackage);
+ mPrewarmSent = true;
+ mContext.sendBroadcast(prewarm);
+ }
+ }
+
+ public void maybeCooldownCamera() {
+ if (!mPrewarmSent) {
+ return;
+ }
+ mPrewarmSent = false;
+ Intent intent = getCameraIntent();
+ String targetPackage = PreviewInflater.getTargetPackage(mContext, intent,
+ mLockPatternUtils.getCurrentUser());
+ if (targetPackage != null) {
+ Intent prewarm = new Intent(MediaStore.ACTION_STILL_IMAGE_CAMERA_COOLDOWN);
+ prewarm.setPackage(targetPackage);
+ mContext.sendBroadcast(prewarm);
+ }
+ }
+
+ public void launchCamera() {
+
+ // Reset prewarm state.
+ mPrewarmSent = false;
+ final Intent intent = getCameraIntent();
boolean wouldLaunchResolverActivity = PreviewInflater.wouldLaunchResolverActivity(
mContext, intent, mLockPatternUtils.getCurrentUser());
if (intent == SECURE_CAMERA_INTENT && !wouldLaunchResolverActivity) {
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ AsyncTask.execute(new Runnable() {
+ @Override
+ public void run() {
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ }
+ });
} else {
// We need to delay starting the activity because ResolverActivity finishes itself if
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 195da46..216730b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -1809,14 +1809,24 @@
}
@Override
- public void onSwipingStarted() {
- mSecureCameraLaunchManager.onSwipingStarted();
+ public void onSwipingStarted(boolean isRightwardMotion) {
+ boolean start = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? isRightwardMotion
+ : !isRightwardMotion;
+ if (!start) {
+ mSecureCameraLaunchManager.onSwipingStarted();
+ mKeyguardBottomArea.prewarmCamera();
+ }
requestDisallowInterceptTouchEvent(true);
mOnlyAffordanceInThisMotion = true;
mQsTracking = false;
}
@Override
+ public void onSwipingAborted() {
+ mKeyguardBottomArea.maybeCooldownCamera();
+ }
+
+ @Override
public KeyguardAffordanceView getLeftIcon() {
return getLayoutDirection() == LAYOUT_DIRECTION_RTL
? mKeyguardBottomArea.getCameraView()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java
index 34068fd..0dce82f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.policy;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -106,15 +107,28 @@
public static boolean wouldLaunchResolverActivity(Context ctx, Intent intent,
int currentUserId) {
+ return getTargetPackage(ctx, intent, currentUserId) == null;
+ }
+
+ /**
+ * @return the target package of the intent it resolves to a specific package or {@code null} if
+ * it resolved to the resolver activity
+ */
+ public static String getTargetPackage(Context ctx, Intent intent,
+ int currentUserId) {
PackageManager packageManager = ctx.getPackageManager();
final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
intent, PackageManager.MATCH_DEFAULT_ONLY, currentUserId);
if (appList.size() == 0) {
- return false;
+ return null;
}
ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA, currentUserId);
- return wouldLaunchResolverActivity(resolved, appList);
+ if (resolved == null || wouldLaunchResolverActivity(resolved, appList)) {
+ return null;
+ } else {
+ return resolved.activityInfo.packageName;
+ }
}
private static boolean wouldLaunchResolverActivity(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
index a3d9377..265e2c6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
@@ -45,6 +45,7 @@
import android.util.SparseArray;
import com.android.systemui.R;
+import com.android.systemui.qs.tiles.DndTile;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -489,8 +490,7 @@
public void showDndTile(boolean visible) {
if (D.BUG) Log.d(TAG, "showDndTile");
- mContext.sendBroadcast(new Intent("com.android.systemui.dndtile.SET_VISIBLE")
- .putExtra("visible", visible));
+ DndTile.setVisible(mContext, visible);
}
private final class VC extends IVolumeController.Stub {
diff --git a/rs/java/android/renderscript/FieldPacker.java b/rs/java/android/renderscript/FieldPacker.java
index 0f967fc..de1c497 100644
--- a/rs/java/android/renderscript/FieldPacker.java
+++ b/rs/java/android/renderscript/FieldPacker.java
@@ -47,6 +47,15 @@
// subAlign() can never work correctly for copied FieldPacker objects.
}
+ static FieldPacker createFromArray(Object[] args) {
+ FieldPacker fp = new FieldPacker(RenderScript.sPointerSize * 8);
+ for (Object arg : args) {
+ fp.addSafely(arg);
+ }
+ fp.resize(fp.mPos);
+ return fp;
+ }
+
public void align(int v) {
if ((v <= 0) || ((v & (v - 1)) != 0)) {
throw new RSIllegalArgumentException("argument must be a non-negative non-zero power of 2: " + v);
@@ -618,294 +627,182 @@
return mPos;
}
- private static void addToPack(FieldPacker fp, Object obj) {
+ private void add(Object obj) {
if (obj instanceof Boolean) {
- fp.addBoolean(((Boolean)obj).booleanValue());
+ addBoolean((Boolean)obj);
return;
}
if (obj instanceof Byte) {
- fp.addI8(((Byte)obj).byteValue());
+ addI8((Byte)obj);
return;
}
if (obj instanceof Short) {
- fp.addI16(((Short)obj).shortValue());
+ addI16((Short)obj);
return;
}
if (obj instanceof Integer) {
- fp.addI32(((Integer)obj).intValue());
+ addI32((Integer)obj);
return;
}
if (obj instanceof Long) {
- fp.addI64(((Long)obj).longValue());
+ addI64((Long)obj);
return;
}
if (obj instanceof Float) {
- fp.addF32(((Float)obj).floatValue());
+ addF32((Float)obj);
return;
}
if (obj instanceof Double) {
- fp.addF64(((Double)obj).doubleValue());
+ addF64((Double)obj);
return;
}
if (obj instanceof Byte2) {
- fp.addI8((Byte2)obj);
+ addI8((Byte2)obj);
return;
}
if (obj instanceof Byte3) {
- fp.addI8((Byte3)obj);
+ addI8((Byte3)obj);
return;
}
if (obj instanceof Byte4) {
- fp.addI8((Byte4)obj);
+ addI8((Byte4)obj);
return;
}
if (obj instanceof Short2) {
- fp.addI16((Short2)obj);
+ addI16((Short2)obj);
return;
}
if (obj instanceof Short3) {
- fp.addI16((Short3)obj);
+ addI16((Short3)obj);
return;
}
if (obj instanceof Short4) {
- fp.addI16((Short4)obj);
+ addI16((Short4)obj);
return;
}
if (obj instanceof Int2) {
- fp.addI32((Int2)obj);
+ addI32((Int2)obj);
return;
}
if (obj instanceof Int3) {
- fp.addI32((Int3)obj);
+ addI32((Int3)obj);
return;
}
if (obj instanceof Int4) {
- fp.addI32((Int4)obj);
+ addI32((Int4)obj);
return;
}
if (obj instanceof Long2) {
- fp.addI64((Long2)obj);
+ addI64((Long2)obj);
return;
}
if (obj instanceof Long3) {
- fp.addI64((Long3)obj);
+ addI64((Long3)obj);
return;
}
if (obj instanceof Long4) {
- fp.addI64((Long4)obj);
+ addI64((Long4)obj);
return;
}
if (obj instanceof Float2) {
- fp.addF32((Float2)obj);
+ addF32((Float2)obj);
return;
}
if (obj instanceof Float3) {
- fp.addF32((Float3)obj);
+ addF32((Float3)obj);
return;
}
if (obj instanceof Float4) {
- fp.addF32((Float4)obj);
+ addF32((Float4)obj);
return;
}
if (obj instanceof Double2) {
- fp.addF64((Double2)obj);
+ addF64((Double2)obj);
return;
}
if (obj instanceof Double3) {
- fp.addF64((Double3)obj);
+ addF64((Double3)obj);
return;
}
if (obj instanceof Double4) {
- fp.addF64((Double4)obj);
+ addF64((Double4)obj);
return;
}
if (obj instanceof Matrix2f) {
- fp.addMatrix((Matrix2f)obj);
+ addMatrix((Matrix2f)obj);
return;
}
if (obj instanceof Matrix3f) {
- fp.addMatrix((Matrix3f)obj);
+ addMatrix((Matrix3f)obj);
return;
}
if (obj instanceof Matrix4f) {
- fp.addMatrix((Matrix4f)obj);
+ addMatrix((Matrix4f)obj);
return;
}
if (obj instanceof BaseObj) {
- fp.addObj((BaseObj)obj);
+ addObj((BaseObj)obj);
return;
}
}
- private static int getPackedSize(Object obj) {
- if (obj instanceof Boolean) {
- return 1;
+ private boolean resize(int newSize) {
+ if (newSize == mLen) {
+ return false;
}
- if (obj instanceof Byte) {
- return 1;
- }
+ byte[] newData = new byte[newSize];
+ System.arraycopy(mData, 0, newData, 0, mPos);
+ mData = newData;
+ mLen = newSize;
+ return true;
+ }
- if (obj instanceof Short) {
- return 2;
- }
-
- if (obj instanceof Integer) {
- return 4;
- }
-
- if (obj instanceof Long) {
- return 8;
- }
-
- if (obj instanceof Float) {
- return 4;
- }
-
- if (obj instanceof Double) {
- return 8;
- }
-
- if (obj instanceof Byte2) {
- return 2;
- }
-
- if (obj instanceof Byte3) {
- return 3;
- }
-
- if (obj instanceof Byte4) {
- return 4;
- }
-
- if (obj instanceof Short2) {
- return 4;
- }
-
- if (obj instanceof Short3) {
- return 6;
- }
-
- if (obj instanceof Short4) {
- return 8;
- }
-
- if (obj instanceof Int2) {
- return 8;
- }
-
- if (obj instanceof Int3) {
- return 12;
- }
-
- if (obj instanceof Int4) {
- return 16;
- }
-
- if (obj instanceof Long2) {
- return 16;
- }
-
- if (obj instanceof Long3) {
- return 24;
- }
-
- if (obj instanceof Long4) {
- return 32;
- }
-
- if (obj instanceof Float2) {
- return 8;
- }
-
- if (obj instanceof Float3) {
- return 12;
- }
-
- if (obj instanceof Float4) {
- return 16;
- }
-
- if (obj instanceof Double2) {
- return 16;
- }
-
- if (obj instanceof Double3) {
- return 24;
- }
-
- if (obj instanceof Double4) {
- return 32;
- }
-
- if (obj instanceof Matrix2f) {
- return 16;
- }
-
- if (obj instanceof Matrix3f) {
- return 36;
- }
-
- if (obj instanceof Matrix4f) {
- return 64;
- }
-
- if (obj instanceof BaseObj) {
- if (RenderScript.sPointerSize == 8) {
- return 32;
- } else {
- return 4;
+ private void addSafely(Object obj) {
+ boolean retry;
+ final int oldPos = mPos;
+ do {
+ retry = false;
+ try {
+ add(obj);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ mPos = oldPos;
+ resize(mLen * 2);
+ retry = true;
}
- }
-
- return 0;
+ } while (retry);
}
- static FieldPacker createFieldPack(Object[] args) {
- int len = 0;
- for (Object arg : args) {
- len += getPackedSize(arg);
- }
- FieldPacker fp = new FieldPacker(len);
- for (Object arg : args) {
- addToPack(fp, arg);
- }
- return fp;
- }
-
- private final byte mData[];
+ private byte mData[];
private int mPos;
private int mLen;
private BitSet mAlignment;
-
}
-
-
diff --git a/rs/java/android/renderscript/ScriptGroup2.java b/rs/java/android/renderscript/ScriptGroup2.java
index 9d73ac4..858a957 100644
--- a/rs/java/android/renderscript/ScriptGroup2.java
+++ b/rs/java/android/renderscript/ScriptGroup2.java
@@ -126,7 +126,7 @@
public Closure(RenderScript rs, Script.InvokeID invokeID,
Object[] args, Map<Script.FieldID, Object> globals) {
super(0, rs);
- mFP = FieldPacker.createFieldPack(args);
+ mFP = FieldPacker.createFromArray(args);
mArgs = args;
mBindings = globals;
@@ -549,11 +549,17 @@
/**
* Creates a script group
*
+ * @param name name for the script group. Legal names can only contain letters, digits,
+ * '-', or '_'. The name can be no longer than 100 characters.
* @param outputs futures intended as outputs of the script group
* @return a script group
*/
- public ScriptGroup2 create(Future... outputs) {
+ public ScriptGroup2 create(String name, Future... outputs) {
+ if (name == null || name.isEmpty() || name.length() > 100 ||
+ !name.equals(name.replaceAll("[^a-zA-Z0-9-]", "_"))) {
+ throw new RSIllegalArgumentException("invalid script group name");
+ }
ScriptGroup2 ret = new ScriptGroup2(mRS, mClosures, mInputs, outputs);
return ret;
}
diff --git a/rs/java/android/renderscript/ScriptIntrinsicBlur.java b/rs/java/android/renderscript/ScriptIntrinsicBlur.java
index 5c4edd3..60e2b6d 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicBlur.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicBlur.java
@@ -34,7 +34,7 @@
* Create an intrinsic for applying a blur to an allocation. The
* default radius is 5.0.
*
- * Supported elements types are {@link Element#U8_4}
+ * Supported elements types are {@link Element#U8_4 Element#U8}
*
* @param rs The RenderScript context
* @param e Element type for inputs and outputs
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 5cc59e5..96840a2 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -94,6 +94,7 @@
import com.android.server.EventLogTags;
import com.android.server.SystemService;
import com.android.server.backup.PackageManagerBackupAgent.Metadata;
+import com.android.server.pm.PackageManagerService;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
diff --git a/services/core/java/com/android/server/AssetAtlasService.java b/services/core/java/com/android/server/AssetAtlasService.java
index f106667..66cc29a 100644
--- a/services/core/java/com/android/server/AssetAtlasService.java
+++ b/services/core/java/com/android/server/AssetAtlasService.java
@@ -415,12 +415,20 @@
new Thread(worker, "Atlas Worker #" + (i + 1)).start();
}
+ boolean isAllWorkerFinished;
try {
- signal.await(10, TimeUnit.SECONDS);
+ isAllWorkerFinished = signal.await(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Log.w(LOG_TAG, "Could not complete configuration computation");
return null;
}
+
+ if (!isAllWorkerFinished) {
+ // We have to abort here, otherwise the async updates on "results" would crash the
+ // sort later.
+ Log.w(LOG_TAG, "Could not complete configuration computation before timeout.");
+ return null;
+ }
}
// Maximize the number of packed bitmaps, minimize the texture size
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 456f1fb..a341c95 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -833,6 +833,9 @@
vol.userId = UserHandle.USER_OWNER;
mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
+ } else if (vol.type == VolumeInfo.TYPE_PRIVATE) {
+ mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
+
} else {
Slog.d(TAG, "Skipping automatic mounting of " + vol);
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 4fa0577..e92443c 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2294,7 +2294,7 @@
EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH,
sr.userId, sr.crashCount, sr.shortName, app.pid);
bringDownServiceLocked(sr);
- } else if (!allowRestart) {
+ } else if (!allowRestart || !mAm.isUserRunningLocked(sr.userId, false)) {
bringDownServiceLocked(sr);
} else {
boolean canceled = scheduleServiceRestartLocked(sr, true);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index cf51004..18ab3b4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -41,7 +41,6 @@
import android.app.IAppTask;
import android.app.ITaskStackListener;
import android.app.ProfilerInfo;
-import android.app.admin.DevicePolicyManager;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManagerInternal;
@@ -429,6 +428,11 @@
*/
ActivityInfo mLastAddedTaskActivity;
+ /**
+ * List of packages whitelisted by DevicePolicyManager for locktask. Indexed by userId.
+ */
+ SparseArray<String[]> mLockTaskPackages = new SparseArray<>();
+
public class PendingAssistExtras extends Binder implements Runnable {
public final ActivityRecord activity;
public final Bundle extras;
@@ -1124,7 +1128,7 @@
boolean mAutoStopProfiler = false;
int mProfileType = 0;
String mOpenGlTraceApp = null;
- final ArrayMap<String, Long> mMemWatchProcesses = new ArrayMap<>();
+ final ProcessMap<Pair<Long, String>> mMemWatchProcesses = new ProcessMap<>();
String mMemWatchDumpProcName;
String mMemWatchDumpFile;
int mMemWatchDumpPid;
@@ -1830,11 +1834,21 @@
final String procName;
final int uid;
final long memLimit;
+ final String reportPackage;
synchronized (ActivityManagerService.this) {
procName = mMemWatchDumpProcName;
uid = mMemWatchDumpUid;
- Long limit = mMemWatchProcesses.get(procName);
- memLimit = limit != null ? limit : 0;
+ Pair<Long, String> val = mMemWatchProcesses.get(procName, uid);
+ if (val == null) {
+ val = mMemWatchProcesses.get(procName, 0);
+ }
+ if (val != null) {
+ memLimit = val.first;
+ reportPackage = val.second;
+ } else {
+ memLimit = 0;
+ reportPackage = null;
+ }
}
if (procName == null) {
return;
@@ -1867,6 +1881,9 @@
intent.setClassName("android", DumpHeapActivity.class.getName());
intent.putExtra(DumpHeapActivity.KEY_PROCESS, procName);
intent.putExtra(DumpHeapActivity.KEY_SIZE, memLimit);
+ if (reportPackage != null) {
+ intent.putExtra(DumpHeapActivity.KEY_DIRECT_LAUNCH, reportPackage);
+ }
int userId = UserHandle.getUserId(uid);
notification.setLatestEventInfo(mContext, text,
mContext.getText(R.string.dump_heap_notification_detail),
@@ -2204,7 +2221,7 @@
mTrackingAssociations = "1".equals(SystemProperties.get("debug.track-associations"));
mConfiguration.setToDefaults();
- mConfiguration.locale = Locale.getDefault();
+ mConfiguration.setLocale(Locale.getDefault());
mConfigurationSeq = mConfiguration.seq = 1;
mProcessCpuTracker.init();
@@ -2474,11 +2491,19 @@
final void setFocusedActivityLocked(ActivityRecord r, String reason) {
if (r != null && mFocusedActivity != r) {
if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedActivityLocked: r=" + r);
+ ActivityRecord last = mFocusedActivity;
mFocusedActivity = r;
if (r.task != null && r.task.voiceInteractor != null) {
startRunningVoiceLocked(r.task.voiceSession, r.info.applicationInfo.uid);
} else {
finishRunningVoiceLocked();
+ if (last != null && last.task.voiceSession != null) {
+ // We had been in a voice interaction session, but now focused has
+ // move to something different. Just finish the session, we can't
+ // return to it and retain the proper state and synchronization with
+ // the voice interaction service.
+ finishVoiceTask(last.task.voiceSession);
+ }
}
if (mStackSupervisor.setFocusedStack(r, reason + " setFocusedActivity")) {
mWindowManager.setFocusedApp(r.appToken, true);
@@ -7798,7 +7823,7 @@
}
if (!allowed) {
Slog.w(TAG, caller + ": caller " + callingUid
- + " does not hold GET_TASKS; limiting output");
+ + " does not hold REAL_GET_TASKS; limiting output");
}
return allowed;
}
@@ -8442,52 +8467,54 @@
}
}
- private boolean isLockTaskAuthorized(String pkg) {
- final DevicePolicyManager dpm = (DevicePolicyManager)
- mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
- try {
- int uid = mContext.getPackageManager().getPackageUid(pkg,
- Binder.getCallingUserHandle().getIdentifier());
- return (uid == Binder.getCallingUid()) && dpm != null && dpm.isLockTaskPermitted(pkg);
- } catch (NameNotFoundException e) {
- return false;
+ @Override
+ public void updateLockTaskPackages(int userId, String[] packages) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("updateLockTaskPackage called from non-system process");
+ }
+ synchronized (this) {
+ mLockTaskPackages.put(userId, packages);
}
}
- void startLockTaskMode(TaskRecord task) {
- final String pkg;
- synchronized (this) {
- pkg = task.intent.getComponent().getPackageName();
+ 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();
// 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;
- if (!isSystemInitiated && !isLockTaskAuthorized(pkg)) {
- StatusBarManagerInternal statusBarManager = LocalServices.getService(
- StatusBarManagerInternal.class);
- if (statusBarManager != null) {
- statusBarManager.showScreenPinningRequest();
- }
- return;
- }
long ident = Binder.clearCallingIdentity();
try {
- synchronized (this) {
- // Since we lost lock on task, make sure it is still there.
- task = mStackSupervisor.anyTaskForIdLocked(task.taskId);
- if (task != null) {
- if (!isSystemInitiated
- && ((mStackSupervisor.getFocusedStack() == null)
- || (task != mStackSupervisor.getFocusedStack().topTask()))) {
- throw new IllegalArgumentException("Invalid task, not in foreground");
- }
- mStackSupervisor.setLockTaskModeLocked(task, isSystemInitiated ?
- ActivityManager.LOCK_TASK_MODE_PINNED :
- ActivityManager.LOCK_TASK_MODE_LOCKED,
- "startLockTask");
+ if (!isSystemInitiated && !isLockTaskAuthorizedLocked(pkg)) {
+ StatusBarManagerInternal statusBarManager =
+ LocalServices.getService(StatusBarManagerInternal.class);
+ if (statusBarManager != null) {
+ statusBarManager.showScreenPinningRequest();
}
+ return;
}
+
+ final ActivityStack stack = mStackSupervisor.getFocusedStack();
+ if (!isSystemInitiated && (stack == null || task != stack.topTask())) {
+ throw new IllegalArgumentException("Invalid task, not in foreground");
+ }
+ mStackSupervisor.setLockTaskModeLocked(task, isSystemInitiated ?
+ ActivityManager.LOCK_TASK_MODE_PINNED :
+ ActivityManager.LOCK_TASK_MODE_LOCKED,
+ "startLockTask");
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -8495,37 +8522,25 @@
@Override
public void startLockTaskMode(int taskId) {
- final TaskRecord task;
- long ident = Binder.clearCallingIdentity();
- try {
- synchronized (this) {
- task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ synchronized (this) {
+ final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ if (task != null) {
+ startLockTaskModeLocked(task);
}
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- if (task != null) {
- startLockTaskMode(task);
}
}
@Override
public void startLockTaskMode(IBinder token) {
- final TaskRecord task;
- long ident = Binder.clearCallingIdentity();
- try {
- synchronized (this) {
- final ActivityRecord r = ActivityRecord.forTokenLocked(token);
- if (r == null) {
- return;
- }
- task = r.task;
+ synchronized (this) {
+ final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ if (r == null) {
+ return;
}
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- if (task != null) {
- startLockTaskMode(task);
+ final TaskRecord task = r.task;
+ if (task != null) {
+ startLockTaskModeLocked(task);
+ }
}
}
@@ -8535,11 +8550,12 @@
"startLockTaskModeOnCurrent");
long ident = Binder.clearCallingIdentity();
try {
- ActivityRecord r = null;
synchronized (this) {
- r = mStackSupervisor.topRunningActivityLocked();
+ ActivityRecord r = mStackSupervisor.topRunningActivityLocked();
+ if (r != null) {
+ startLockTaskModeLocked(r.task);
+ }
}
- startLockTaskMode(r.task);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -12019,16 +12035,23 @@
public List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses() {
enforceNotIsolatedCaller("getRunningAppProcesses");
+
+ final int callingUid = Binder.getCallingUid();
+
// Lazy instantiation of list
List<ActivityManager.RunningAppProcessInfo> runList = null;
final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL,
- Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED;
- int userId = UserHandle.getUserId(Binder.getCallingUid());
+ callingUid) == PackageManager.PERMISSION_GRANTED;
+ final int userId = UserHandle.getUserId(callingUid);
+ final boolean allUids = isGetTasksAllowed(
+ "getRunningAppProcesses", Binder.getCallingPid(), callingUid);
+
synchronized (this) {
// Iterate across all processes
- for (int i=mLruProcesses.size()-1; i>=0; i--) {
+ for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord app = mLruProcesses.get(i);
- if (!allUsers && app.userId != userId) {
+ if ((!allUsers && app.userId != userId)
+ || (!allUids && app.uid != callingUid)) {
continue;
}
if ((app.thread != null) && (!app.crashing && !app.notResponding)) {
@@ -12052,7 +12075,7 @@
//Slog.v(TAG, "Proc " + app.processName + ": imp=" + currApp.importance
// + " lru=" + currApp.lru);
if (runList == null) {
- runList = new ArrayList<ActivityManager.RunningAppProcessInfo>();
+ runList = new ArrayList<>();
}
runList.add(currApp);
}
@@ -12855,16 +12878,28 @@
+ " mOrigWaitForDebugger=" + mOrigWaitForDebugger);
}
}
- if (mMemWatchProcesses.size() > 0) {
+ if (mMemWatchProcesses.getMap().size() > 0) {
pw.println(" Mem watch processes:");
- for (int i=0; i<mMemWatchProcesses.size(); i++) {
- if (needSep) {
- pw.println();
- needSep = false;
+ final ArrayMap<String, SparseArray<Pair<Long, String>>> procs
+ = mMemWatchProcesses.getMap();
+ for (int i=0; i<procs.size(); i++) {
+ final String proc = procs.keyAt(i);
+ final SparseArray<Pair<Long, String>> uids = procs.valueAt(i);
+ for (int j=0; j<uids.size(); j++) {
+ if (needSep) {
+ pw.println();
+ needSep = false;
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append(" ").append(proc).append('/');
+ UserHandle.formatUid(sb, uids.keyAt(j));
+ Pair<Long, String> val = uids.valueAt(i);
+ sb.append(": "); DebugUtils.sizeValueToString(val.first, sb);
+ if (val.second != null) {
+ sb.append(", report to ").append(val.second);
+ }
+ pw.println(sb.toString());
}
- pw.print(" "); pw.print(mMemWatchProcesses.keyAt(i));
- pw.print(": "); DebugUtils.printSizeValue(pw, mMemWatchProcesses.valueAt(i));
- pw.println();
}
pw.print(" mMemWatchDumpProcName="); pw.println(mMemWatchDumpProcName);
pw.print(" mMemWatchDumpFile="); pw.println(mMemWatchDumpFile);
@@ -17389,7 +17424,18 @@
proc.lastCachedPss = pss;
}
- Long check = mMemWatchProcesses.get(proc.processName);
+ final SparseArray<Pair<Long, String>> watchUids
+ = mMemWatchProcesses.getMap().get(proc.processName);
+ Long check = null;
+ if (watchUids != null) {
+ Pair<Long, String> val = watchUids.get(proc.uid);
+ if (val == null) {
+ val = watchUids.get(0);
+ }
+ if (val != null) {
+ check = val.first;
+ }
+ }
if (check != null) {
if ((pss * 1024) >= check && proc.thread != null && mMemWatchDumpProcName == null) {
boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
@@ -17425,7 +17471,8 @@
IApplicationThread thread = myProc.thread;
if (thread != null) {
try {
- if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting dump heap from "
+ if (true || DEBUG_PSS) Slog.d(TAG_PSS,
+ "Requesting dump heap from "
+ myProc + " to " + heapdumpFile);
thread.dumpHeap(true, heapdumpFile.toString(), fd);
} catch (RemoteException e) {
@@ -18660,15 +18707,38 @@
}
@Override
- public void setDumpHeapDebugLimit(String processName, long maxMemSize) {
- enforceCallingPermission(android.Manifest.permission.SET_DEBUG_APP,
- "setDumpHeapDebugLimit()");
+ public void setDumpHeapDebugLimit(String processName, int uid, long maxMemSize,
+ String reportPackage) {
+ if (processName != null) {
+ enforceCallingPermission(android.Manifest.permission.SET_DEBUG_APP,
+ "setDumpHeapDebugLimit()");
+ } else {
+ if (!Build.IS_DEBUGGABLE) {
+ throw new SecurityException("Not running a debuggable build");
+ }
+ synchronized (mPidsSelfLocked) {
+ ProcessRecord proc = mPidsSelfLocked.get(Binder.getCallingPid());
+ if (proc == null) {
+ throw new SecurityException("No process found for calling pid "
+ + Binder.getCallingPid());
+ }
+ processName = proc.processName;
+ uid = proc.uid;
+ if (reportPackage != null && !proc.pkgList.containsKey(reportPackage)) {
+ throw new SecurityException("Package " + reportPackage + " is not running in "
+ + proc);
+ }
+ }
+ }
synchronized (this) {
if (maxMemSize > 0) {
- mMemWatchProcesses.put(processName, maxMemSize);
- mHandler.sendEmptyMessage(POST_DUMP_HEAP_NOTIFICATION_MSG);
+ mMemWatchProcesses.put(processName, uid, new Pair(maxMemSize, reportPackage));
} else {
- mMemWatchProcesses.remove(processName);
+ if (uid != 0) {
+ mMemWatchProcesses.remove(processName, uid);
+ } else {
+ mMemWatchProcesses.getMap().remove(processName);
+ }
}
}
}
@@ -18686,7 +18756,7 @@
+ " does not match last path " + mMemWatchDumpFile);
return;
}
- if (DEBUG_PSS) Slog.d(TAG_PSS, "Dump heap finished for " + path);
+ if (true || DEBUG_PSS) Slog.d(TAG_PSS, "Dump heap finished for " + path);
mHandler.sendEmptyMessage(POST_DUMP_HEAP_NOTIFICATION_MSG);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 1108391..ce510a9 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -16,6 +16,8 @@
package com.android.server.am;
+import static android.content.pm.ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN;
+
import static com.android.server.am.ActivityManagerDebugConfig.*;
import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
@@ -373,8 +375,7 @@
}
boolean okToShowLocked(ActivityRecord r) {
- return isCurrentProfileLocked(r.userId)
- || (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0;
+ return isCurrentProfileLocked(r.userId) || (r.info.flags & FLAG_SHOW_ON_LOCK_SCREEN) != 0;
}
final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
@@ -617,13 +618,15 @@
final int userId = UserHandle.getUserId(info.applicationInfo.uid);
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- TaskRecord task = mTaskHistory.get(taskNdx);
- if (!isCurrentProfileLocked(task.userId)) {
- return null;
- }
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ final boolean notCurrentUserTask = !isCurrentProfileLocked(task.userId);
final ArrayList<ActivityRecord> activities = task.mActivities;
+
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord r = activities.get(activityNdx);
+ if (notCurrentUserTask && (r.info.flags & FLAG_SHOW_ON_LOCK_SCREEN) == 0) {
+ return null;
+ }
if (!r.finishing && r.intent.getComponent().equals(cls) && r.userId == userId) {
//Slog.i(TAG, "Found matching class!");
//dump();
@@ -648,8 +651,12 @@
// Move userId's tasks to the top.
int index = mTaskHistory.size();
for (int i = 0; i < index; ) {
- TaskRecord task = mTaskHistory.get(i);
- if (isCurrentProfileLocked(task.userId)) {
+ final TaskRecord task = mTaskHistory.get(i);
+
+ // NOTE: If {@link TaskRecord#topRunningActivityLocked} return is not null then it is
+ // okay to show the activity when locked.
+ if (isCurrentProfileLocked(task.userId)
+ || task.topRunningActivityLocked(null) != null) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "switchUserLocked: stack=" + getStackId() +
" moving " + task + " to top");
mTaskHistory.remove(i);
@@ -1263,6 +1270,7 @@
// make sure any activities under it are now visible.
boolean aboveTop = true;
boolean behindFullscreen = !isStackVisibleLocked();
+ boolean noStackActivityResumed = (isInStackLocked(starting) == null);
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
@@ -1290,9 +1298,8 @@
}
if (r.app == null || r.app.thread == null) {
- // This activity needs to be visible, but isn't even
- // running... get it started, but don't resume it
- // at this point.
+ // This activity needs to be visible, but isn't even running...
+ // get it started and resume if no other stack in this stack is resumed.
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
"Start and freeze screen for " + r);
if (r != starting) {
@@ -1304,7 +1311,9 @@
setVisible(r, true);
}
if (r != starting) {
- mStackSupervisor.startSpecificActivityLocked(r, false, false);
+ mStackSupervisor.startSpecificActivityLocked(
+ r, noStackActivityResumed, false);
+ noStackActivityResumed = false;
}
} else if (r.visible) {
@@ -1981,7 +1990,7 @@
return null;
}
- private void insertTaskAtTop(TaskRecord task) {
+ private void insertTaskAtTop(TaskRecord task, ActivityRecord newActivity) {
// If the moving task is over home stack, transfer its return type to next task
if (task.isOverHomeStack()) {
final TaskRecord nextTask = getNextTask(task);
@@ -2009,10 +2018,15 @@
mTaskHistory.remove(task);
// Now put task at top.
int taskNdx = mTaskHistory.size();
- if (!isCurrentProfileLocked(task.userId)) {
+ final boolean notShownWhenLocked =
+ (newActivity != null && (newActivity.info.flags & FLAG_SHOW_ON_LOCK_SCREEN) == 0)
+ || (newActivity == null && task.topRunningActivityLocked(null) == null);
+ if (!isCurrentProfileLocked(task.userId) && notShownWhenLocked) {
// Put non-current user tasks below current user tasks.
while (--taskNdx >= 0) {
- if (!isCurrentProfileLocked(mTaskHistory.get(taskNdx).userId)) {
+ final TaskRecord tmpTask = mTaskHistory.get(taskNdx);
+ if (!isCurrentProfileLocked(tmpTask.userId)
+ || tmpTask.topRunningActivityLocked(null) == null) {
break;
}
}
@@ -2031,7 +2045,7 @@
// Last activity in task had been removed or ActivityManagerService is reusing task.
// Insert or replace.
// Might not even be in.
- insertTaskAtTop(rTask);
+ insertTaskAtTop(rTask, r);
mWindowManager.moveTaskToTop(taskId);
}
TaskRecord task = null;
@@ -3611,7 +3625,7 @@
// Shift all activities with this task up to the top
// of the stack, keeping them in the same internal order.
- insertTaskAtTop(tr);
+ insertTaskAtTop(tr, null);
// Set focus to the top running activity of this stack.
ActivityRecord r = topRunningActivityLocked(null);
@@ -4034,16 +4048,18 @@
}
void getTasksLocked(List<RunningTaskInfo> list, int callingUid, boolean allowed) {
+ boolean focusedStack = mStackSupervisor.getFocusedStack() == this;
+ boolean topTask = true;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
+ if (task.getTopActivity() == null) {
+ continue;
+ }
ActivityRecord r = null;
ActivityRecord top = null;
int numActivities = 0;
int numRunning = 0;
final ArrayList<ActivityRecord> activities = task.mActivities;
- if (activities.isEmpty()) {
- continue;
- }
if (!allowed && !task.isHomeTask() && task.effectiveUid != callingUid) {
continue;
}
@@ -4072,14 +4088,18 @@
ci.baseActivity = r.intent.getComponent();
ci.topActivity = top.intent.getComponent();
ci.lastActiveTime = task.lastActiveTime;
+ if (focusedStack && topTask) {
+ // Give the latest time to ensure foreground task can be sorted
+ // at the first, because lastActiveTime of creating task is 0.
+ ci.lastActiveTime = System.currentTimeMillis();
+ topTask = false;
+ }
if (top.task != null) {
ci.description = top.task.lastDescription;
}
ci.numActivities = numActivities;
ci.numRunning = numRunning;
- //System.out.println(
- // "#" + maxNum + ": " + " descr=" + ci.description);
list.add(ci);
}
}
@@ -4288,7 +4308,7 @@
void addTask(final TaskRecord task, final boolean toTop, boolean moving) {
task.stack = this;
if (toTop) {
- insertTaskAtTop(task);
+ insertTaskAtTop(task, null);
} else {
mTaskHistory.add(0, task);
updateTaskMovement(task, false);
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index f05e6aa..4ccb5ad 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -63,8 +63,10 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Date;
import java.util.Iterator;
import java.util.Stack;
+import java.text.DateFormat;
/**
* @hide
@@ -124,6 +126,8 @@
}
protected void dump(PrintWriter pw) {
+ pw.println("\nMediaFocusControl dump time: "
+ + DateFormat.getTimeInstance().format(new Date()));
dumpFocusStack(pw);
dumpRCStack(pw);
dumpRCCStack(pw);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 43f74fc..4ac2b48 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -785,6 +785,8 @@
HdmiDeviceInfo avr = getAvrDeviceInfo();
if (avr != null) {
onNewAvrAdded(avr);
+ } else {
+ setSystemAudioMode(false, true);
}
}
});
@@ -1615,10 +1617,6 @@
removeAction(SystemAudioAutoInitiationAction.class);
removeAction(SystemAudioStatusAction.class);
removeAction(VolumeControlAction.class);
-
- // Turn off the mode but do not write it the settings, so that the next time TV powers on
- // the system audio mode setting can be restored automatically.
- setSystemAudioMode(false, false);
}
@ServiceThreadOnly
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index 63c8d92..53ceb2e 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -153,8 +153,9 @@
}
mRunningJob = job;
- mParams = new JobParameters(this, job.getJobId(), job.getExtras(),
- !job.isConstraintsSatisfied());
+ final boolean isDeadlineExpired =
+ job.getLatestRunTimeElapsed() >= SystemClock.elapsedRealtime();
+ mParams = new JobParameters(this, job.getJobId(), job.getExtras(), isDeadlineExpired);
mExecutionStartTimeElapsed = SystemClock.elapsedRealtime();
mVerb = VERB_BINDING;
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
index 03e9ad5..8a9f3e1 100644
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -42,7 +42,7 @@
implements UsageStatsManagerInternal.AppIdleStateChangeListener {
private static final String LOG_TAG = "AppIdleController";
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
// Singleton factory
private static Object sCreationLock = new Object();
diff --git a/services/core/java/com/android/server/job/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java
index 4c6cb17..b3d7287 100644
--- a/services/core/java/com/android/server/job/controllers/TimeController.java
+++ b/services/core/java/com/android/server/job/controllers/TimeController.java
@@ -91,14 +91,20 @@
public synchronized void maybeStartTrackingJob(JobStatus job) {
if (job.hasTimingDelayConstraint() || job.hasDeadlineConstraint()) {
maybeStopTrackingJob(job);
+ boolean isInsert = false;
ListIterator<JobStatus> it = mTrackedJobs.listIterator(mTrackedJobs.size());
while (it.hasPrevious()) {
JobStatus ts = it.previous();
if (ts.getLatestRunTimeElapsed() < job.getLatestRunTimeElapsed()) {
// Insert
+ isInsert = true;
break;
}
}
+ if(isInsert)
+ {
+ it.next();
+ }
it.add(job);
maybeUpdateAlarms(
job.hasTimingDelayConstraint() ? job.getEarliestRunTime() : Long.MAX_VALUE,
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 95d7a52..591dbee 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -31,16 +31,22 @@
import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.app.ActivityManager;
+import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PackageDeleteObserver;
import android.app.PackageInstallObserver;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
+import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageInstallerCallback;
import android.content.pm.IPackageInstallerSession;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageInstaller.SessionParams;
@@ -63,6 +69,8 @@
import android.os.SELinux;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
@@ -75,15 +83,17 @@
import android.util.SparseBooleanArray;
import android.util.Xml;
+import libcore.io.IoUtils;
+
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageHelper;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.ImageUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.IoThread;
import com.google.android.collect.Sets;
-import libcore.io.IoUtils;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -131,6 +141,7 @@
private static final String ATTR_ORIGINATING_URI = "originatingUri";
private static final String ATTR_REFERRER_URI = "referrerUri";
private static final String ATTR_ABI_OVERRIDE = "abiOverride";
+ private static final String ATTR_VOLUME_UUID = "volumeUuid";
/** Automatically destroy sessions older than this */
private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS;
@@ -141,9 +152,10 @@
private final Context mContext;
private final PackageManagerService mPm;
- private final AppOpsManager mAppOps;
- private final File mStagingDir;
+ private AppOpsManager mAppOps;
+ private StorageManager mStorage;
+
private final HandlerThread mInstallThread;
private final Handler mInstallHandler;
@@ -186,12 +198,9 @@
}
};
- public PackageInstallerService(Context context, PackageManagerService pm, File stagingDir) {
+ public PackageInstallerService(Context context, PackageManagerService pm) {
mContext = context;
mPm = pm;
- mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
-
- mStagingDir = stagingDir;
mInstallThread = new HandlerThread(TAG);
mInstallThread.start();
@@ -208,8 +217,9 @@
synchronized (mSessions) {
readSessionsLocked();
+ final File internalStagingDir = buildInternalStagingDir();
final ArraySet<File> unclaimedStages = Sets.newArraySet(
- mStagingDir.listFiles(sStageFilter));
+ internalStagingDir.listFiles(sStageFilter));
final ArraySet<File> unclaimedIcons = Sets.newArraySet(
mSessionsDir.listFiles());
@@ -238,6 +248,11 @@
}
}
+ public void systemReady() {
+ mAppOps = mContext.getSystemService(AppOpsManager.class);
+ mStorage = mContext.getSystemService(StorageManager.class);
+ }
+
public void onSecureContainersAvailable() {
synchronized (mSessions) {
final ArraySet<String> unclaimed = new ArraySet<>();
@@ -275,13 +290,13 @@
}
@Deprecated
- public File allocateInternalStageDirLegacy() throws IOException {
+ public File allocateStageDirLegacy(String volumeUuid) throws IOException {
synchronized (mSessions) {
try {
final int sessionId = allocateSessionIdLocked();
mLegacySessions.put(sessionId, true);
- final File stageDir = buildInternalStageDir(sessionId);
- prepareInternalStageDir(stageDir);
+ final File stageDir = buildStageDir(volumeUuid, sessionId);
+ prepareStageDir(stageDir);
return stageDir;
} catch (IllegalStateException e) {
throw new IOException(e);
@@ -322,11 +337,6 @@
Slog.w(TAG, "Abandoning old session first created at "
+ session.createdMillis);
valid = false;
- } else if (session.stageDir != null
- && !session.stageDir.exists()) {
- Slog.w(TAG, "Abandoning internal session with missing stage "
- + session.stageDir);
- valid = false;
} else {
valid = true;
}
@@ -378,6 +388,7 @@
params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI);
params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
+ params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
final File appIconFile = buildAppIconFile(sessionId);
if (appIconFile.exists()) {
@@ -448,6 +459,7 @@
writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri);
writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
+ writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
// Persist app icon if changed since last written
final File appIconFile = buildAppIconFile(session.sessionId);
@@ -528,28 +540,40 @@
}
}
- if (params.mode == SessionParams.MODE_FULL_INSTALL
- || params.mode == SessionParams.MODE_INHERIT_EXISTING) {
+ switch (params.mode) {
+ case SessionParams.MODE_FULL_INSTALL:
+ case SessionParams.MODE_INHERIT_EXISTING:
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid install mode: " + params.mode);
+ }
+
+ // If caller requested explicit location, sanity check it, otherwise
+ // resolve the best internal or adopted location.
+ if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
+ if (!PackageHelper.fitsOnInternal(mContext, params.sizeBytes)) {
+ throw new IOException("No suitable internal storage available");
+ }
+
+ } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
+ if (!PackageHelper.fitsOnExternal(mContext, params.sizeBytes)) {
+ throw new IOException("No suitable external storage available");
+ }
+
+ } else {
+ // For now, installs to adopted media are treated as internal from
+ // an install flag point-of-view.
+ params.setInstallFlagsInternal();
+
// Resolve best location for install, based on combination of
// requested install flags, delta size, and manifest settings.
final long ident = Binder.clearCallingIdentity();
try {
- final int resolved = PackageHelper.resolveInstallLocation(mContext,
- params.appPackageName, params.installLocation, params.sizeBytes,
- params.installFlags);
-
- if (resolved == PackageHelper.RECOMMEND_INSTALL_INTERNAL) {
- params.setInstallFlagsInternal();
- } else if (resolved == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
- params.setInstallFlagsExternal();
- } else {
- throw new IOException("No storage with enough free space; res=" + resolved);
- }
+ params.volumeUuid = PackageHelper.resolveInstallVolume(mContext,
+ params.appPackageName, params.installLocation, params.sizeBytes);
} finally {
Binder.restoreCallingIdentity(ident);
}
- } else {
- throw new IllegalArgumentException("Invalid install mode: " + params.mode);
}
final int sessionId;
@@ -574,7 +598,7 @@
File stageDir = null;
String stageCid = null;
if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
- stageDir = buildInternalStageDir(sessionId);
+ stageDir = buildStageDir(params.volumeUuid, sessionId);
} else {
stageCid = buildExternalStageCid(sessionId);
}
@@ -673,11 +697,30 @@
throw new IllegalStateException("Failed to allocate session ID");
}
- private File buildInternalStageDir(int sessionId) {
- return new File(mStagingDir, "vmdl" + sessionId + ".tmp");
+ private File buildInternalStagingDir() {
+ return new File(Environment.getDataDirectory(), "app");
}
- static void prepareInternalStageDir(File stageDir) throws IOException {
+ private File buildStagingDir(String volumeUuid) throws FileNotFoundException {
+ if (volumeUuid == null) {
+ return buildInternalStagingDir();
+ } else {
+ final VolumeInfo vol = mStorage.findVolumeByUuid(volumeUuid);
+ if (vol != null && vol.type == VolumeInfo.TYPE_PRIVATE
+ && vol.state == VolumeInfo.STATE_MOUNTED) {
+ return new File(vol.path, "app");
+ } else {
+ throw new FileNotFoundException("Failed to find volume for UUID " + volumeUuid);
+ }
+ }
+ }
+
+ private File buildStageDir(String volumeUuid, int sessionId) throws FileNotFoundException {
+ final File stagingDir = buildStagingDir(volumeUuid);
+ return new File(stagingDir, "vmdl" + sessionId + ".tmp");
+ }
+
+ static void prepareStageDir(File stageDir) throws IOException {
if (stageDir.exists()) {
throw new IOException("Session dir already exists: " + stageDir);
}
@@ -749,16 +792,34 @@
}
@Override
- public void uninstall(String packageName, int flags, IntentSender statusReceiver, int userId) {
- mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, true, "uninstall");
+ public void uninstall(String packageName, String callerPackageName, int flags,
+ IntentSender statusReceiver, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
+ if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
+ mAppOps.checkPackage(callingUid, callerPackageName);
+ }
+
+ // Check whether the caller is device owner
+ DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+ boolean isDeviceOwner = (dpm != null) && dpm.isDeviceOwnerApp(callerPackageName);
final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
- statusReceiver, packageName);
+ statusReceiver, packageName, isDeviceOwner, userId);
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
== PackageManager.PERMISSION_GRANTED) {
// Sweet, call straight through!
mPm.deletePackage(packageName, adapter.getBinder(), userId, flags);
-
+ } else if (isDeviceOwner) {
+ // Allow the DeviceOwner to silently delete packages
+ // Need to clear the calling identity to get DELETE_PACKAGES permission
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mPm.deletePackage(packageName, adapter.getBinder(), userId, flags);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
} else {
// Take a short detour to confirm with user
final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
@@ -814,12 +875,21 @@
private final Context mContext;
private final IntentSender mTarget;
private final String mPackageName;
+ private final Notification mNotification;
public PackageDeleteObserverAdapter(Context context, IntentSender target,
- String packageName) {
+ String packageName, boolean showNotification, int userId) {
mContext = context;
mTarget = target;
mPackageName = packageName;
+ if (showNotification) {
+ mNotification = buildSuccessNotification(mContext,
+ mContext.getResources().getString(R.string.package_deleted_device_owner),
+ packageName,
+ userId);
+ } else {
+ mNotification = null;
+ }
}
@Override
@@ -837,6 +907,11 @@
@Override
public void onPackageDeleted(String basePackageName, int returnCode, String msg) {
+ if (PackageManager.DELETE_SUCCEEDED == returnCode && mNotification != null) {
+ NotificationManager notificationManager = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ notificationManager.notify(basePackageName, 0, mNotification);
+ }
final Intent fillIn = new Intent();
fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName);
fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
@@ -855,11 +930,16 @@
private final Context mContext;
private final IntentSender mTarget;
private final int mSessionId;
+ private final boolean mShowNotification;
+ private final int mUserId;
- public PackageInstallObserverAdapter(Context context, IntentSender target, int sessionId) {
+ public PackageInstallObserverAdapter(Context context, IntentSender target, int sessionId,
+ boolean showNotification, int userId) {
mContext = context;
mTarget = target;
mSessionId = sessionId;
+ mShowNotification = showNotification;
+ mUserId = userId;
}
@Override
@@ -878,6 +958,17 @@
@Override
public void onPackageInstalled(String basePackageName, int returnCode, String msg,
Bundle extras) {
+ if (PackageManager.INSTALL_SUCCEEDED == returnCode && mShowNotification) {
+ Notification notification = buildSuccessNotification(mContext,
+ mContext.getResources().getString(R.string.package_installed_device_owner),
+ basePackageName,
+ mUserId);
+ if (notification != null) {
+ NotificationManager notificationManager = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ notificationManager.notify(basePackageName, 0, notification);
+ }
+ }
final Intent fillIn = new Intent();
fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
@@ -899,6 +990,40 @@
}
}
+ /**
+ * Build a notification for package installation / deletion by device owners that is shown if
+ * the operation succeeds.
+ */
+ private static Notification buildSuccessNotification(Context context, String contentText,
+ String basePackageName, int userId) {
+ PackageInfo packageInfo = null;
+ try {
+ packageInfo = AppGlobals.getPackageManager().getPackageInfo(
+ basePackageName, 0, userId);
+ } catch (RemoteException ignored) {
+ }
+ if (packageInfo == null || packageInfo.applicationInfo == null) {
+ Slog.w(TAG, "Notification not built for package: " + basePackageName);
+ return null;
+ }
+ PackageManager pm = context.getPackageManager();
+ Bitmap packageIcon = ImageUtils.buildScaledBitmap(
+ packageInfo.applicationInfo.loadIcon(pm),
+ context.getResources().getDimensionPixelSize(
+ android.R.dimen.notification_large_icon_width),
+ context.getResources().getDimensionPixelSize(
+ android.R.dimen.notification_large_icon_height));
+ CharSequence packageLabel = packageInfo.applicationInfo.loadLabel(pm);
+ return new Notification.Builder(context)
+ .setSmallIcon(R.drawable.ic_check_circle_24px)
+ .setColor(context.getResources().getColor(
+ R.color.system_notification_accent_color))
+ .setContentTitle(packageLabel)
+ .setContentText(contentText)
+ .setLargeIcon(packageIcon)
+ .build();
+ }
+
private static class Callbacks extends Handler {
private static final int MSG_SESSION_CREATED = 1;
private static final int MSG_SESSION_BADGING_CHANGED = 2;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index cc1b3ad..46db2d8 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -25,8 +25,9 @@
import static android.system.OsConstants.O_RDONLY;
import static android.system.OsConstants.O_WRONLY;
import static com.android.server.pm.PackageInstallerService.prepareExternalStageCid;
-import static com.android.server.pm.PackageInstallerService.prepareInternalStageDir;
+import static com.android.server.pm.PackageInstallerService.prepareStageDir;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
@@ -61,6 +62,9 @@
import android.util.MathUtils;
import android.util.Slog;
+import libcore.io.IoUtils;
+import libcore.io.Libcore;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
@@ -69,9 +73,6 @@
import com.android.internal.util.Preconditions;
import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
-import libcore.io.IoUtils;
-import libcore.io.Libcore;
-
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -92,6 +93,7 @@
private final Context mContext;
private final PackageManagerService mPm;
private final Handler mHandler;
+ private final boolean mIsInstallerDeviceOwner;
final int sessionId;
final int userId;
@@ -208,8 +210,15 @@
mPrepared = prepared;
mSealed = sealed;
+ // Device owners are allowed to silently install packages, so the permission check is
+ // waived if the installer is the device owner.
+ DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+ mIsInstallerDeviceOwner = (dpm != null) && dpm.isDeviceOwnerApp(installerPackageName);
if ((mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid)
- == PackageManager.PERMISSION_GRANTED) || (installerUid == Process.ROOT_UID)) {
+ == PackageManager.PERMISSION_GRANTED)
+ || (installerUid == Process.ROOT_UID)
+ || mIsInstallerDeviceOwner) {
mPermissionsAccepted = true;
} else {
mPermissionsAccepted = false;
@@ -440,7 +449,7 @@
mActiveCount.incrementAndGet();
final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
- statusReceiver, sessionId);
+ statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);
mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
}
@@ -892,7 +901,7 @@
synchronized (mLock) {
if (!mPrepared) {
if (stageDir != null) {
- prepareInternalStageDir(stageDir);
+ prepareStageDir(stageDir);
} else if (stageCid != null) {
prepareExternalStageCid(stageCid, params.sizeBytes);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3c99484..4fea889 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -43,11 +43,17 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_USER_RESTRICTED;
import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
import static android.content.pm.PackageManager.INSTALL_FORWARD_LOCK;
+import static android.content.pm.PackageManager.INSTALL_INTERNAL;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+import static android.content.pm.PackageManager.MOVE_EXTERNAL_MEDIA;
+import static android.content.pm.PackageManager.MOVE_FAILED_DOESNT_EXIST;
+import static android.content.pm.PackageManager.MOVE_FAILED_OPERATION_PENDING;
+import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
+import static android.content.pm.PackageManager.MOVE_INTERNAL;
import static android.content.pm.PackageParser.isApkFile;
import static android.os.Process.PACKAGE_INFO_GID;
import static android.os.Process.SYSTEM_UID;
@@ -65,30 +71,7 @@
import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
import android.Manifest;
-import android.content.pm.IntentFilterVerificationInfo;
-import android.util.ArrayMap;
-
-import com.android.internal.R;
-import com.android.internal.app.IMediaContainerService;
-import com.android.internal.app.ResolverActivity;
-import com.android.internal.content.NativeLibraryHelper;
-import com.android.internal.content.PackageHelper;
-import com.android.internal.os.IParcelFileDescriptorFactory;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FastPrintWriter;
-import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.EventLogTags;
-import com.android.server.IntentResolver;
-import com.android.server.LocalServices;
-import com.android.server.ServiceThread;
-import com.android.server.SystemConfig;
-import com.android.server.Watchdog;
-import com.android.server.pm.Settings.DatabaseVersion;
-import com.android.server.storage.DeviceStorageMonitorInternal;
-
-import org.xmlpull.v1.XmlSerializer;
-
+import org.xmlpull.v1.XmlPullParser;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
@@ -118,6 +101,7 @@
import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstrumentationInfo;
+import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.KeySet;
import android.content.pm.ManifestDigest;
import android.content.pm.PackageCleanItem;
@@ -126,10 +110,10 @@
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
+import android.content.pm.PackageParser;
import android.content.pm.PackageParser.ActivityIntentInfo;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser;
import android.content.pm.PackageStats;
import android.content.pm.PackageUserState;
import android.content.pm.ParceledListSlice;
@@ -149,13 +133,9 @@
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.Debug;
import android.os.Environment;
import android.os.Environment.UserEnvironment;
-import android.os.storage.IMountService;
-import android.os.storage.StorageEventListener;
-import android.os.storage.StorageManager;
-import android.os.storage.VolumeInfo;
-import android.os.Debug;
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
@@ -171,6 +151,10 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.storage.IMountService;
+import android.os.storage.StorageEventListener;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
import android.security.KeyStore;
import android.security.SystemKeyStore;
import android.system.ErrnoException;
@@ -178,6 +162,7 @@
import android.system.StructStat;
import android.text.TextUtils;
import android.text.format.DateUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.DisplayMetrics;
@@ -189,11 +174,41 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.Xml;
import android.view.Display;
+import dalvik.system.DexFile;
+import dalvik.system.VMRuntime;
+
+import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
+
+import com.android.internal.R;
+import com.android.internal.app.IMediaContainerService;
+import com.android.internal.app.ResolverActivity;
+import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.content.PackageHelper;
+import com.android.internal.os.IParcelFileDescriptorFactory;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.EventLogTags;
+import com.android.server.IntentResolver;
+import com.android.server.LocalServices;
+import com.android.server.ServiceThread;
+import com.android.server.SystemConfig;
+import com.android.server.Watchdog;
+import com.android.server.pm.Settings.DatabaseVersion;
+import com.android.server.storage.DeviceStorageMonitorInternal;
+
+import org.xmlpull.v1.XmlSerializer;
+
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
@@ -223,12 +238,6 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
-import dalvik.system.DexFile;
-import dalvik.system.VMRuntime;
-
-import libcore.io.IoUtils;
-import libcore.util.EmptyArray;
-
/**
* Keep track of all those .apks everywhere.
*
@@ -246,6 +255,7 @@
static final boolean DEBUG_SETTINGS = false;
static final boolean DEBUG_PREFERRED = false;
static final boolean DEBUG_UPGRADE = false;
+ private static final boolean DEBUG_BACKUP = true;
private static final boolean DEBUG_INSTALL = false;
private static final boolean DEBUG_REMOVE = false;
private static final boolean DEBUG_BROADCASTS = false;
@@ -257,8 +267,7 @@
private static final boolean DEBUG_DEXOPT = false;
private static final boolean DEBUG_ABI_SELECTION = false;
- static final boolean RUNTIME_PERMISSIONS_ENABLED =
- SystemProperties.getInt("ro.runtime.permissions.enabled", 0) == 1;
+ static final boolean RUNTIME_PERMISSIONS_ENABLED = true;
private static final int RADIO_UID = Process.PHONE_UID;
private static final int LOG_UID = Process.LOG_UID;
@@ -866,6 +875,9 @@
final SparseArray<PostInstallData> mRunningInstalls = new SparseArray<PostInstallData>();
int mNextInstallToken = 1; // nonzero; will be wrapped back to 1 when ++ overflows
+ // backup/restore of preferred activity state
+ private static final String TAG_PREFERRED_BACKUP = "pa";
+
private final String mRequiredVerifierPackage;
private final PackageUsage mPackageUsage = new PackageUsage();
@@ -1531,9 +1543,14 @@
private StorageEventListener mStorageListener = new StorageEventListener() {
@Override
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
- Slog.v(TAG, vol.toString());
+ if (vol.type == VolumeInfo.TYPE_PRIVATE) {
+ if (vol.state == VolumeInfo.STATE_MOUNTED) {
+ loadPrivatePackages(vol);
+ } else if (vol.state == VolumeInfo.STATE_UNMOUNTING) {
+ unloadPrivatePackages(vol);
+ }
+ }
- // TODO: when private volume shows up, look for packages there too
if (vol.isPrimary() && vol.type == VolumeInfo.TYPE_PUBLIC) {
if (vol.state == VolumeInfo.STATE_MOUNTED) {
updateExternalMediaStatus(true, false);
@@ -2152,7 +2169,7 @@
mRequiredVerifierPackage = getRequiredVerifierLPr();
- mInstallerService = new PackageInstallerService(context, this, mAppInstallDir);
+ mInstallerService = new PackageInstallerService(context, this);
mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr();
mIntentFilterVerifier = new IntentVerifierProxy(mContext,
@@ -8468,8 +8485,8 @@
public void installPackage(String originPath, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, VerificationParams verificationParams,
String packageAbiOverride) {
- installPackageAsUser(originPath, observer, installFlags, installerPackageName, verificationParams,
- packageAbiOverride, UserHandle.getCallingUserId());
+ installPackageAsUser(originPath, observer, installFlags, installerPackageName,
+ verificationParams, packageAbiOverride, UserHandle.getCallingUserId());
}
@Override
@@ -8516,7 +8533,7 @@
final Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new InstallParams(origin, observer, installFlags,
- installerPackageName, verificationParams, user, packageAbiOverride);
+ installerPackageName, null, verificationParams, user, packageAbiOverride);
mHandler.sendMessage(msg);
}
@@ -8535,7 +8552,7 @@
final Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new InstallParams(origin, observer, params.installFlags,
- installerPackageName, verifParams, user, params.abiOverride);
+ installerPackageName, params.volumeUuid, verifParams, user, params.abiOverride);
mHandler.sendMessage(msg);
}
@@ -9337,19 +9354,21 @@
final IPackageInstallObserver2 observer;
int installFlags;
final String installerPackageName;
+ final String volumeUuid;
final VerificationParams verificationParams;
private InstallArgs mArgs;
private int mRet;
final String packageAbiOverride;
InstallParams(OriginInfo origin, IPackageInstallObserver2 observer, int installFlags,
- String installerPackageName, VerificationParams verificationParams, UserHandle user,
- String packageAbiOverride) {
+ String installerPackageName, String volumeUuid,
+ VerificationParams verificationParams, UserHandle user, String packageAbiOverride) {
super(user);
this.origin = origin;
this.observer = observer;
this.installFlags = installFlags;
this.installerPackageName = installerPackageName;
+ this.volumeUuid = volumeUuid;
this.verificationParams = verificationParams;
this.packageAbiOverride = packageAbiOverride;
}
@@ -9703,7 +9722,7 @@
* @param installFlags package installation flags
* @return true if should be installed on external storage
*/
- private static boolean installOnSd(int installFlags) {
+ private static boolean installOnExternalAsec(int installFlags) {
if ((installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
return false;
}
@@ -9724,7 +9743,7 @@
}
private InstallArgs createInstallArgs(InstallParams params) {
- if (installOnSd(params.installFlags) || params.isForwardLocked()) {
+ if (installOnExternalAsec(params.installFlags) || params.isForwardLocked()) {
return new AsecInstallArgs(params);
} else {
return new FileInstallArgs(params);
@@ -9738,7 +9757,7 @@
private InstallArgs createInstallArgsForExisting(int installFlags, String codePath,
String resourcePath, String nativeLibraryRoot, String[] instructionSets) {
final boolean isInAsec;
- if (installOnSd(installFlags)) {
+ if (installOnExternalAsec(installFlags)) {
/* Apps on SD card are always in ASEC containers. */
isInAsec = true;
} else if (installForwardLocked(installFlags)
@@ -9754,7 +9773,7 @@
if (isInAsec) {
return new AsecInstallArgs(codePath, instructionSets,
- installOnSd(installFlags), installForwardLocked(installFlags));
+ installOnExternalAsec(installFlags), installForwardLocked(installFlags));
} else {
return new FileInstallArgs(codePath, resourcePath, nativeLibraryRoot,
instructionSets);
@@ -9769,6 +9788,7 @@
// Always refers to PackageManager flags only
final int installFlags;
final String installerPackageName;
+ final String volumeUuid;
final ManifestDigest manifestDigest;
final UserHandle user;
final String abiOverride;
@@ -9779,12 +9799,13 @@
/* nullable */ String[] instructionSets;
InstallArgs(OriginInfo origin, IPackageInstallObserver2 observer, int installFlags,
- String installerPackageName, ManifestDigest manifestDigest, UserHandle user,
- String[] instructionSets, String abiOverride) {
+ String installerPackageName, String volumeUuid, ManifestDigest manifestDigest,
+ UserHandle user, String[] instructionSets, String abiOverride) {
this.origin = origin;
this.installFlags = installFlags;
this.observer = observer;
this.installerPackageName = installerPackageName;
+ this.volumeUuid = volumeUuid;
this.manifestDigest = manifestDigest;
this.user = user;
this.instructionSets = instructionSets;
@@ -9836,7 +9857,7 @@
return (installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
}
- protected boolean isExternal() {
+ protected boolean isExternalAsec() {
return (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
}
@@ -9883,8 +9904,8 @@
/** New install */
FileInstallArgs(InstallParams params) {
super(params.origin, params.observer, params.installFlags,
- params.installerPackageName, params.getManifestDigest(), params.getUser(),
- null /* instruction sets */, params.packageAbiOverride);
+ params.installerPackageName, params.volumeUuid, params.getManifestDigest(),
+ params.getUser(), null /* instruction sets */, params.packageAbiOverride);
if (isFwdLocked()) {
throw new IllegalArgumentException("Forward locking only supported in ASEC");
}
@@ -9893,7 +9914,7 @@
/** Existing install */
FileInstallArgs(String codePath, String resourcePath, String legacyNativeLibraryPath,
String[] instructionSets) {
- super(OriginInfo.fromNothing(), null, 0, null, null, null, instructionSets, null);
+ super(OriginInfo.fromNothing(), null, 0, null, null, null, null, instructionSets, null);
this.codeFile = (codePath != null) ? new File(codePath) : null;
this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
this.legacyNativeLibraryPath = (legacyNativeLibraryPath != null) ?
@@ -9917,7 +9938,7 @@
}
try {
- final File tempDir = mInstallerService.allocateInternalStageDirLegacy();
+ final File tempDir = mInstallerService.allocateStageDirLegacy(volumeUuid);
codeFile = tempDir;
resourceFile = tempDir;
} catch (IOException e) {
@@ -9978,8 +9999,9 @@
cleanUp();
return false;
} else {
+ final File targetDir = codeFile.getParentFile();
final File beforeCodeFile = codeFile;
- final File afterCodeFile = getNextCodePath(pkg.packageName);
+ final File afterCodeFile = getNextCodePath(targetDir, pkg.packageName);
Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile);
try {
@@ -10129,16 +10151,15 @@
/** New install */
AsecInstallArgs(InstallParams params) {
super(params.origin, params.observer, params.installFlags,
- params.installerPackageName, params.getManifestDigest(),
- params.getUser(), null /* instruction sets */,
- params.packageAbiOverride);
+ params.installerPackageName, params.volumeUuid, params.getManifestDigest(),
+ params.getUser(), null /* instruction sets */, params.packageAbiOverride);
}
/** Existing install */
AsecInstallArgs(String fullCodePath, String[] instructionSets,
boolean isExternal, boolean isForwardLocked) {
super(OriginInfo.fromNothing(), null, (isExternal ? INSTALL_EXTERNAL : 0)
- | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
+ | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, null,
instructionSets, null);
// Hackily pretend we're still looking at a full code path
if (!fullCodePath.endsWith(RES_FILE_NAME)) {
@@ -10155,7 +10176,7 @@
AsecInstallArgs(String cid, String[] instructionSets, boolean isForwardLocked) {
super(OriginInfo.fromNothing(), null, (isAsecExternal(cid) ? INSTALL_EXTERNAL : 0)
- | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
+ | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, null,
instructionSets, null);
this.cid = cid;
setMountPath(PackageHelper.getSdDir(cid));
@@ -10170,7 +10191,7 @@
abiOverride);
final File target;
- if (isExternal()) {
+ if (isExternalAsec()) {
target = new UserEnvironment(UserHandle.USER_OWNER).getExternalStorageDirectory();
} else {
target = Environment.getDataDirectory();
@@ -10199,7 +10220,7 @@
}
final String newMountPath = imcs.copyPackageToContainer(
- origin.file.getAbsolutePath(), cid, getEncryptKey(), isExternal(),
+ origin.file.getAbsolutePath(), cid, getEncryptKey(), isExternalAsec(),
isFwdLocked(), deriveAbiOverride(abiOverride, null /* settings */));
if (newMountPath != null) {
@@ -10480,11 +10501,11 @@
return prefix + idxStr;
}
- private File getNextCodePath(String packageName) {
+ private File getNextCodePath(File targetDir, String packageName) {
int suffix = 1;
File result;
do {
- result = new File(mAppInstallDir, packageName + "-" + suffix);
+ result = new File(targetDir, packageName + "-" + suffix);
suffix++;
} while (result.exists());
return result;
@@ -10548,9 +10569,9 @@
/*
* Install a non-existing package.
*/
- private void installNewPackageLI(PackageParser.Package pkg,
- int parseFlags, int scanFlags, UserHandle user,
- String installerPackageName, PackageInstalledInfo res) {
+ private void installNewPackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags,
+ UserHandle user, String installerPackageName, String volumeUuid,
+ PackageInstalledInfo res) {
// Remember this for later, in case we need to rollback this install
String pkgName = pkg.packageName;
@@ -10579,7 +10600,7 @@
PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanFlags,
System.currentTimeMillis(), user);
- updateSettingsLI(newPackage, installerPackageName, null, null, res, user);
+ updateSettingsLI(newPackage, installerPackageName, volumeUuid, null, null, res, user);
// delete the partially installed application. the data directory will have to be
// restored if it was already existing
if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
@@ -10611,9 +10632,9 @@
return false;
}
- private void replacePackageLI(PackageParser.Package pkg,
- int parseFlags, int scanFlags, UserHandle user,
- String installerPackageName, PackageInstalledInfo res) {
+ private void replacePackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags,
+ UserHandle user, String installerPackageName, String volumeUuid,
+ PackageInstalledInfo res) {
PackageParser.Package oldPackage;
String pkgName = pkg.packageName;
int[] allUsers;
@@ -10652,17 +10673,17 @@
boolean sysPkg = (isSystemApp(oldPackage));
if (sysPkg) {
replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanFlags,
- user, allUsers, perUserInstalled, installerPackageName, res);
+ user, allUsers, perUserInstalled, installerPackageName, volumeUuid, res);
} else {
replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanFlags,
- user, allUsers, perUserInstalled, installerPackageName, res);
+ user, allUsers, perUserInstalled, installerPackageName, volumeUuid, res);
}
}
private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage,
PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user,
- int[] allUsers, boolean[] perUserInstalled,
- String installerPackageName, PackageInstalledInfo res) {
+ int[] allUsers, boolean[] perUserInstalled, String installerPackageName,
+ String volumeUuid, PackageInstalledInfo res) {
String pkgName = deletedPackage.packageName;
boolean deletedPkg = true;
boolean updatedSettings = false;
@@ -10701,8 +10722,8 @@
try {
final PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags,
scanFlags | SCAN_UPDATE_TIME, System.currentTimeMillis(), user);
- updateSettingsLI(newPackage, installerPackageName, allUsers, perUserInstalled, res,
- user);
+ updateSettingsLI(newPackage, installerPackageName, volumeUuid, allUsers,
+ perUserInstalled, res, user);
updatedSettings = true;
} catch (PackageManagerException e) {
res.setError("Package couldn't be installed in " + pkg.codePath, e);
@@ -10727,10 +10748,10 @@
if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + deletedPackage);
File restoreFile = new File(deletedPackage.codePath);
// Parse old package
- boolean oldOnSd = isExternal(deletedPackage);
+ boolean oldExternal = isExternal(deletedPackage);
int oldParseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY |
(deletedPackage.isForwardLocked() ? PackageParser.PARSE_FORWARD_LOCK : 0) |
- (oldOnSd ? PackageParser.PARSE_ON_SDCARD : 0);
+ (oldExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;
try {
scanPackageLI(restoreFile, oldParseFlags, oldScanFlags, origUpdateTime, null);
@@ -10754,8 +10775,8 @@
private void replaceSystemPackageLI(PackageParser.Package deletedPackage,
PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user,
- int[] allUsers, boolean[] perUserInstalled,
- String installerPackageName, PackageInstalledInfo res) {
+ int[] allUsers, boolean[] perUserInstalled, String installerPackageName,
+ String volumeUuid, PackageInstalledInfo res) {
if (DEBUG_INSTALL) Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg
+ ", old=" + deletedPackage);
boolean disabledSystem = false;
@@ -10832,8 +10853,8 @@
}
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
- updateSettingsLI(newPackage, installerPackageName, allUsers, perUserInstalled, res,
- user);
+ updateSettingsLI(newPackage, installerPackageName, volumeUuid, allUsers,
+ perUserInstalled, res, user);
updatedSettings = true;
}
@@ -10868,8 +10889,8 @@
}
private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
- int[] allUsers, boolean[] perUserInstalled,
- PackageInstalledInfo res, UserHandle user) {
+ String volumeUuid, int[] allUsers, boolean[] perUserInstalled, PackageInstalledInfo res,
+ UserHandle user) {
String pkgName = newPackage.packageName;
synchronized (mPackages) {
//write settings. the installStatus will be incomplete at this stage.
@@ -10927,6 +10948,7 @@
res.pkg = newPackage;
mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE);
mSettings.setInstallerPackageName(pkgName, installerPackageName);
+ mSettings.setVolumeUuid(pkgName, volumeUuid);
res.returnCode = PackageManager.INSTALL_SUCCEEDED;
//to update install status
mSettings.writeLPr();
@@ -10935,10 +10957,12 @@
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
final int installFlags = args.installFlags;
- String installerPackageName = args.installerPackageName;
- File tmpPackageFile = new File(args.getCodePath());
- boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
- boolean onSd = ((installFlags & PackageManager.INSTALL_EXTERNAL) != 0);
+ final String installerPackageName = args.installerPackageName;
+ final String volumeUuid = args.volumeUuid;
+ final File tmpPackageFile = new File(args.getCodePath());
+ final boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
+ final boolean onExternal = (((installFlags & PackageManager.INSTALL_EXTERNAL) != 0)
+ || (args.volumeUuid != null));
boolean replace = false;
final int scanFlags = SCAN_NEW_INSTALL | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE;
// Result object to be returned
@@ -10948,7 +10972,7 @@
// Retrieve PackageSettings and parse package
final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
| (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
- | (onSd ? PackageParser.PARSE_ON_SDCARD : 0);
+ | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setDisplayMetrics(mMetrics);
@@ -11100,7 +11124,7 @@
}
- if (systemApp && onSd) {
+ if (systemApp && onExternal) {
// Disable updates to system apps on sdcard
res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
"Cannot install updates to system apps on sdcard");
@@ -11123,13 +11147,13 @@
startIntentFilterVerifications(args.user.getIdentifier(), pkg);
+ // Call with SCAN_NO_DEX, since dexopt has already been made
if (replace) {
- // Call replacePackageLI with SCAN_NO_DEX, since we already made dexopt
replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING | SCAN_NO_DEX, args.user,
- installerPackageName, res);
+ installerPackageName, volumeUuid, res);
} else {
- installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
- args.user, installerPackageName, res);
+ installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES
+ | SCAN_NO_DEX, args.user, installerPackageName, volumeUuid, res);
}
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(pkgName);
@@ -11257,7 +11281,9 @@
private int packageFlagsToInstallFlags(PackageSetting ps) {
int installFlags = 0;
- if (isExternal(ps)) {
+ if (isExternal(ps) && TextUtils.isEmpty(ps.volumeUuid)) {
+ // This existing package was an external ASEC install when we have
+ // the external flag without a UUID
installFlags |= PackageManager.INSTALL_EXTERNAL;
}
if (ps.isForwardLocked()) {
@@ -12525,6 +12551,83 @@
}
}
+ /**
+ * Non-Binder method, support for the backup/restore mechanism: write the
+ * full set of preferred activities in its canonical XML format. Returns true
+ * on success; false otherwise.
+ */
+ @Override
+ public byte[] getPreferredActivityBackup(int userId) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Only the system may call getPreferredActivityBackup()");
+ }
+
+ ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
+ try {
+ final XmlSerializer serializer = new FastXmlSerializer();
+ serializer.setOutput(dataStream, "utf-8");
+ serializer.startDocument(null, true);
+ serializer.startTag(null, TAG_PREFERRED_BACKUP);
+
+ synchronized (mPackages) {
+ mSettings.writePreferredActivitiesLPr(serializer, userId, true);
+ }
+
+ serializer.endTag(null, TAG_PREFERRED_BACKUP);
+ serializer.endDocument();
+ serializer.flush();
+ } catch (Exception e) {
+ if (DEBUG_BACKUP) {
+ Slog.e(TAG, "Unable to write preferred activities for backup", e);
+ }
+ return null;
+ }
+
+ return dataStream.toByteArray();
+ }
+
+ @Override
+ public void restorePreferredActivities(byte[] backup, int userId) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Only the system may call restorePreferredActivities()");
+ }
+
+ try {
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new ByteArrayInputStream(backup), null);
+
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ }
+ if (type != XmlPullParser.START_TAG) {
+ // oops didn't find a start tag?!
+ if (DEBUG_BACKUP) {
+ Slog.e(TAG, "Didn't find start tag during restore");
+ }
+ return;
+ }
+
+ // this is supposed to be TAG_PREFERRED_BACKUP
+ if (!TAG_PREFERRED_BACKUP.equals(parser.getName())) {
+ if (DEBUG_BACKUP) {
+ Slog.e(TAG, "Found unexpected tag " + parser.getName());
+ }
+ return;
+ }
+
+ // skip interfering stuff, then we're aligned with the backing implementation
+ while ((type = parser.next()) == XmlPullParser.TEXT) { }
+ synchronized (mPackages) {
+ mSettings.readPreferredActivitiesLPw(parser, userId);
+ }
+ } catch (Exception e) {
+ if (DEBUG_BACKUP) {
+ Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
+ }
+ }
+ }
+
@Override
public void addCrossProfileIntentFilter(IntentFilter intentFilter, String ownerPackage,
int sourceUserId, int targetUserId, int flags) {
@@ -12890,6 +12993,8 @@
// Watch for external volumes that come and go over time
final StorageManager storage = mContext.getSystemService(StorageManager.class);
storage.registerListener(mStorageListener);
+
+ mInstallerService.systemReady();
}
@Override
@@ -13628,13 +13733,32 @@
}
private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
+ ArrayList<ApplicationInfo> infos, IIntentReceiver finishedReceiver) {
+ final int size = infos.size();
+ final String[] packageNames = new String[size];
+ final int[] packageUids = new int[size];
+ for (int i = 0; i < size; i++) {
+ final ApplicationInfo info = infos.get(i);
+ packageNames[i] = info.packageName;
+ packageUids[i] = info.uid;
+ }
+ sendResourcesChangedBroadcast(mediaStatus, replacing, packageNames, packageUids,
+ finishedReceiver);
+ }
+
+ private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
ArrayList<String> pkgList, int uidArr[], IIntentReceiver finishedReceiver) {
- int size = pkgList.size();
+ sendResourcesChangedBroadcast(mediaStatus, replacing,
+ pkgList.toArray(new String[pkgList.size()]), uidArr, finishedReceiver);
+ }
+
+ private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
+ String[] pkgList, int uidArr[], IIntentReceiver finishedReceiver) {
+ int size = pkgList.length;
if (size > 0) {
// Send broadcasts here
Bundle extras = new Bundle();
- extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList
- .toArray(new String[size]));
+ extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
if (uidArr != null) {
extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr);
}
@@ -13677,8 +13801,8 @@
}
// Parse package
int parseFlags = mDefParseFlags;
- if (args.isExternal()) {
- parseFlags |= PackageParser.PARSE_ON_SDCARD;
+ if (args.isExternalAsec()) {
+ parseFlags |= PackageParser.PARSE_EXTERNAL_STORAGE;
}
if (args.isFwdLocked()) {
parseFlags |= PackageParser.PARSE_FORWARD_LOCK;
@@ -13825,73 +13949,130 @@
}
}
- /** Binder call */
+ private void loadPrivatePackages(VolumeInfo vol) {
+ final ArrayList<ApplicationInfo> loaded = new ArrayList<>();
+ final int parseFlags = mDefParseFlags | PackageParser.PARSE_EXTERNAL_STORAGE;
+ synchronized (mPackages) {
+ final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(vol.fsUuid);
+ for (PackageSetting ps : packages) {
+ synchronized (mInstallLock) {
+ final PackageParser.Package pkg;
+ try {
+ pkg = scanPackageLI(ps.codePath, parseFlags, 0, 0, null);
+ loaded.add(pkg.applicationInfo);
+ } catch (PackageManagerException e) {
+ Slog.w(TAG, "Failed to scan " + ps.codePath + ": " + e.getMessage());
+ }
+ }
+ }
+
+ // TODO: regrant any permissions that changed based since original install
+
+ mSettings.writeLPr();
+ }
+
+ Slog.d(TAG, "Loaded packages " + loaded);
+ sendResourcesChangedBroadcast(true, false, loaded, null);
+ }
+
+ private void unloadPrivatePackages(VolumeInfo vol) {
+ final ArrayList<ApplicationInfo> unloaded = new ArrayList<>();
+ synchronized (mPackages) {
+ final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(vol.fsUuid);
+ for (PackageSetting ps : packages) {
+ if (ps.pkg == null) continue;
+ synchronized (mInstallLock) {
+ final ApplicationInfo info = ps.pkg.applicationInfo;
+ final PackageRemovedInfo outInfo = new PackageRemovedInfo();
+ if (deletePackageLI(ps.name, null, false, null, null,
+ PackageManager.DELETE_KEEP_DATA, outInfo, false)) {
+ unloaded.add(info);
+ } else {
+ Slog.w(TAG, "Failed to unload " + ps.codePath);
+ }
+ }
+ }
+
+ mSettings.writeLPr();
+ }
+
+ Slog.d(TAG, "Unloaded packages " + unloaded);
+ sendResourcesChangedBroadcast(false, false, unloaded, null);
+ }
+
@Override
public void movePackage(final String packageName, final IPackageMoveObserver observer,
final int flags) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null);
- UserHandle user = new UserHandle(UserHandle.getCallingUserId());
- int returnCode = PackageManager.MOVE_SUCCEEDED;
- int currInstallFlags = 0;
- int newInstallFlags = 0;
+
+ final int installFlags;
+ if ((flags & MOVE_INTERNAL) != 0) {
+ installFlags = INSTALL_INTERNAL;
+ } else if ((flags & MOVE_EXTERNAL_MEDIA) != 0) {
+ installFlags = INSTALL_EXTERNAL;
+ } else {
+ throw new IllegalArgumentException("Unsupported move flags " + flags);
+ }
+
+ try {
+ movePackageInternal(packageName, null, installFlags, false, observer);
+ } catch (PackageManagerException e) {
+ Slog.d(TAG, "Failed to move " + packageName, e);
+ try {
+ observer.packageMoved(packageName, e.error);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ @Override
+ public void movePackageAndData(final String packageName, final String volumeUuid,
+ final IPackageMoveObserver observer) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null);
+ try {
+ movePackageInternal(packageName, volumeUuid, INSTALL_INTERNAL, true, observer);
+ } catch (PackageManagerException e) {
+ Slog.d(TAG, "Failed to move " + packageName, e);
+ try {
+ observer.packageMoved(packageName, e.error);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ private void movePackageInternal(final String packageName, String volumeUuid, int installFlags,
+ boolean andData, final IPackageMoveObserver observer) throws PackageManagerException {
+ final UserHandle user = new UserHandle(UserHandle.getCallingUserId());
File codeFile = null;
String installerPackageName = null;
String packageAbiOverride = null;
+ // TOOD: move app private data before installing
+
// reader
synchronized (mPackages) {
final PackageParser.Package pkg = mPackages.get(packageName);
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (pkg == null || ps == null) {
- returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
- } else {
- // Disable moving fwd locked apps and system packages
- if (pkg.applicationInfo != null && isSystemApp(pkg)) {
- Slog.w(TAG, "Cannot move system application");
- returnCode = PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
- } else if (pkg.mOperationPending) {
- Slog.w(TAG, "Attempt to move package which has pending operations");
- returnCode = PackageManager.MOVE_FAILED_OPERATION_PENDING;
- } else {
- // Find install location first
- if ((flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0
- && (flags & PackageManager.MOVE_INTERNAL) != 0) {
- Slog.w(TAG, "Ambigous flags specified for move location.");
- returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION;
- } else {
- newInstallFlags = (flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0
- ? PackageManager.INSTALL_EXTERNAL : PackageManager.INSTALL_INTERNAL;
- currInstallFlags = isExternal(pkg)
- ? PackageManager.INSTALL_EXTERNAL : PackageManager.INSTALL_INTERNAL;
-
- if (newInstallFlags == currInstallFlags) {
- Slog.w(TAG, "No move required. Trying to move to same location");
- returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION;
- } else {
- if (pkg.isForwardLocked()) {
- currInstallFlags |= PackageManager.INSTALL_FORWARD_LOCK;
- newInstallFlags |= PackageManager.INSTALL_FORWARD_LOCK;
- }
- }
- }
- if (returnCode == PackageManager.MOVE_SUCCEEDED) {
- pkg.mOperationPending = true;
- }
- }
-
- codeFile = new File(pkg.codePath);
- installerPackageName = ps.installerPackageName;
- packageAbiOverride = ps.cpuAbiOverrideString;
+ throw new PackageManagerException(MOVE_FAILED_DOESNT_EXIST, "Missing package");
}
- }
- if (returnCode != PackageManager.MOVE_SUCCEEDED) {
- try {
- observer.packageMoved(packageName, returnCode);
- } catch (RemoteException ignored) {
+ if (pkg.applicationInfo.isSystemApp()) {
+ throw new PackageManagerException(MOVE_FAILED_SYSTEM_PACKAGE,
+ "Cannot move system application");
+ } else if (pkg.mOperationPending) {
+ throw new PackageManagerException(MOVE_FAILED_OPERATION_PENDING,
+ "Attempt to move package which has pending operations");
}
- return;
+
+ // TODO: yell if already in desired location
+
+ pkg.mOperationPending = true;
+
+ codeFile = new File(pkg.codePath);
+ installerPackageName = ps.installerPackageName;
+ packageAbiOverride = ps.cpuAbiOverrideString;
}
final IPackageInstallObserver2 installObserver = new IPackageInstallObserver2.Stub() {
@@ -13933,12 +14114,12 @@
// Treat a move like reinstalling an existing app, which ensures that we
// process everythign uniformly, like unpacking native libraries.
- newInstallFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
+ installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
final Message msg = mHandler.obtainMessage(INIT_COPY);
final OriginInfo origin = OriginInfo.fromExistingFile(codeFile);
- msg.obj = new InstallParams(origin, installObserver, newInstallFlags,
- installerPackageName, null, user, packageAbiOverride);
+ msg.obj = new InstallParams(origin, installObserver, installFlags,
+ installerPackageName, volumeUuid, null, user, packageAbiOverride);
mHandler.sendMessage(msg);
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index daa6d64..f294b32 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -23,6 +23,7 @@
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageUserState;
+import android.os.storage.VolumeInfo;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -108,8 +109,10 @@
PackageSettingBase origPackage;
- /* package name of the app that installed this package */
+ /** Package name of the app that installed this package */
String installerPackageName;
+ /** UUID of {@link VolumeInfo} hosting this app */
+ String volumeUuid;
IntentFilterVerificationInfo verificationInfo;
@@ -161,6 +164,7 @@
origPackage = base.origPackage;
installerPackageName = base.installerPackageName;
+ volumeUuid = base.volumeUuid;
keySetData = new PackageKeySetData(base.keySetData);
}
@@ -183,10 +187,18 @@
installerPackageName = packageName;
}
- String getInstallerPackageName() {
+ public String getInstallerPackageName() {
return installerPackageName;
}
+ public void setVolumeUuid(String volumeUuid) {
+ this.volumeUuid = volumeUuid;
+ }
+
+ public String getVolumeUuid() {
+ return volumeUuid;
+ }
+
public void setInstallStatus(int newStatus) {
installStatus = newStatus;
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 0d2ef89..6b7c35c 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -42,18 +42,21 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.storage.VolumeInfo;
import android.util.AtomicFile;
import android.text.TextUtils;
import android.util.LogPrinter;
-
import android.util.SparseBooleanArray;
import android.util.SparseLongArray;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.JournaledFile;
+import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
+import com.android.server.backup.PreferredActivityBackupHelper;
import com.android.server.pm.PackageManagerService.DumpState;
import java.io.FileNotFoundException;
@@ -332,14 +335,20 @@
}
}
- void setInstallerPackageName(String pkgName,
- String installerPkgName) {
+ void setInstallerPackageName(String pkgName, String installerPkgName) {
PackageSetting p = mPackages.get(pkgName);
- if(p != null) {
+ if (p != null) {
p.setInstallerPackageName(installerPkgName);
}
}
+ void setVolumeUuid(String pkgName, String volumeUuid) {
+ PackageSetting p = mPackages.get(pkgName);
+ if (p != null) {
+ p.setVolumeUuid(volumeUuid);
+ }
+ }
+
SharedUserSetting getSharedUserLPw(String name,
int pkgFlags, int pkgPrivateFlags, boolean create) {
SharedUserSetting s = mSharedUsers.get(name);
@@ -1108,7 +1117,13 @@
mExternalDatabaseVersion = CURRENT_DATABASE_VERSION;
}
- private void readPreferredActivitiesLPw(XmlPullParser parser, int userId)
+ /**
+ * Applies the preferred activity state described by the given XML. This code
+ * also supports the restore-from-backup code path.
+ *
+ * @see PreferredActivityBackupHelper
+ */
+ void readPreferredActivitiesLPw(XmlPullParser parser, int userId)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
@@ -1399,6 +1414,11 @@
return components;
}
+ /**
+ * Record the state of preferred activity configuration into XML. This is used both
+ * for recording packages.xml internally and for supporting backup/restore of the
+ * preferred activity configuration.
+ */
void writePreferredActivitiesLPr(XmlSerializer serializer, int userId, boolean full)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(null, "preferred-activities");
@@ -2054,6 +2074,9 @@
if (pkg.installerPackageName != null) {
serializer.attribute(null, "installer", pkg.installerPackageName);
}
+ if (pkg.volumeUuid != null) {
+ serializer.attribute(null, "volumeUuid", pkg.volumeUuid);
+ }
pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
if ((pkg.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissions());
@@ -2896,6 +2919,7 @@
String cpuAbiOverrideString = null;
String systemStr = null;
String installerPackageName = null;
+ String volumeUuid = null;
String uidError = null;
int pkgFlags = 0;
int pkgPrivateFlags = 0;
@@ -2933,6 +2957,7 @@
}
}
installerPackageName = parser.getAttributeValue(null, "installer");
+ volumeUuid = parser.getAttributeValue(null, "volumeUuid");
systemStr = parser.getAttributeValue(null, "publicFlags");
if (systemStr != null) {
@@ -3081,6 +3106,7 @@
if (packageSetting != null) {
packageSetting.uidError = "true".equals(uidError);
packageSetting.installerPackageName = installerPackageName;
+ packageSetting.volumeUuid = volumeUuid;
packageSetting.legacyNativeLibraryPathString = legacyNativeLibraryPathStr;
packageSetting.primaryCpuAbiString = primaryCpuAbiString;
packageSetting.secondaryCpuAbiString = secondaryCpuAbiString;
@@ -3537,6 +3563,22 @@
return null;
}
+ /**
+ * Return all {@link PackageSetting} that are actively installed on the
+ * given {@link VolumeInfo#fsUuid}.
+ */
+ List<PackageSetting> getVolumePackagesLPr(String volumeUuid) {
+ Preconditions.checkNotNull(volumeUuid);
+ ArrayList<PackageSetting> res = new ArrayList<>();
+ for (int i = 0; i < mPackages.size(); i++) {
+ final PackageSetting setting = mPackages.valueAt(i);
+ if (Objects.equals(volumeUuid, setting.volumeUuid)) {
+ res.add(setting);
+ }
+ }
+ return res;
+ }
+
static void printFlags(PrintWriter pw, int val, Object[] spec) {
pw.print("[ ");
for (int i=0; i<spec.length; i+=2) {
@@ -3743,6 +3785,10 @@
pw.print(prefix); pw.print(" installerPackageName=");
pw.println(ps.installerPackageName);
}
+ if (ps.volumeUuid != null) {
+ pw.print(prefix); pw.print(" volumeUuid=");
+ pw.println(ps.volumeUuid);
+ }
pw.print(prefix); pw.print(" signatures="); pw.println(ps.signatures);
pw.print(prefix); pw.print(" installPermissionsFixed=");
pw.print(ps.installPermissionsFixed);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 98ac5ef..34120a0 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -107,6 +107,11 @@
}
}
+ boolean showWhenLocked() {
+ final int tokensCount = mAppTokens.size();
+ return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showWhenLocked;
+ }
+
@Override
public String toString() {
return "{taskId=" + mTaskId + " appTokens=" + mAppTokens + " mdr=" + mDeferRemoval + "}";
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index b61a6f7..e845f83 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -275,21 +275,29 @@
return false;
}
+ void addTask(Task task, boolean toTop) {
+ addTask(task, toTop, task.showWhenLocked());
+ }
+
/**
* Put a Task in this stack. Used for adding and moving.
* @param task The task to add.
* @param toTop Whether to add it to the top or bottom.
+ * @param showWhenLocked Whether to show the task when the device is locked
+ * regardless of the current user.
*/
- void addTask(Task task, boolean toTop) {
+ void addTask(Task task, boolean toTop, boolean showWhenLocked) {
int stackNdx;
if (!toTop) {
stackNdx = 0;
} else {
stackNdx = mTasks.size();
- if (!mService.isCurrentProfileLocked(task.mUserId)) {
+ if (!showWhenLocked && !mService.isCurrentProfileLocked(task.mUserId)) {
// Place the task below all current user tasks.
while (--stackNdx >= 0) {
- if (!mService.isCurrentProfileLocked(mTasks.get(stackNdx).mUserId)) {
+ final Task tmpTask = mTasks.get(stackNdx);
+ if (!tmpTask.showWhenLocked()
+ || !mService.isCurrentProfileLocked(tmpTask.mUserId)) {
break;
}
}
@@ -490,7 +498,7 @@
int top = mTasks.size();
for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
Task task = mTasks.get(taskNdx);
- if (mService.isCurrentProfileLocked(task.mUserId)) {
+ if (mService.isCurrentProfileLocked(task.mUserId) || task.showWhenLocked()) {
mTasks.remove(taskNdx);
mTasks.add(task);
--top;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d365759..7fab5a6 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -617,8 +617,8 @@
static final long WALLPAPER_TIMEOUT_RECOVERY = 10000;
boolean mAnimateWallpaperWithTarget;
- // We give a wallpaper up to 1000ms to finish drawing before playing app transitions.
- static final long WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION = 1000;
+ // We give a wallpaper up to 500ms to finish drawing before playing app transitions.
+ static final long WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION = 500;
static final int WALLPAPER_DRAW_NORMAL = 0;
static final int WALLPAPER_DRAW_PENDING = 1;
static final int WALLPAPER_DRAW_TIMEOUT = 2;
@@ -3620,7 +3620,7 @@
EventLog.writeEvent(EventLogTags.WM_TASK_CREATED, taskId, stackId);
Task task = new Task(taskId, stack, userId, this);
mTaskIdToTask.put(taskId, task);
- stack.addTask(task, !atoken.mLaunchTaskBehind /* toTop */);
+ stack.addTask(task, !atoken.mLaunchTaskBehind /* toTop */, atoken.showWhenLocked);
return task;
}
@@ -9064,41 +9064,40 @@
goodToGo = false;
}
}
-// Stuck in a state with mWallpaperDrawState == WALLPAPER_DRAW_PENDING without a timeout. Leave
-// commented out until that is understood.
-// if (goodToGo && isWallpaperVisible(mWallpaperTarget)) {
-// boolean wallpaperGoodToGo = true;
-// for (int curTokenIndex = mWallpaperTokens.size() - 1;
-// curTokenIndex >= 0 && wallpaperGoodToGo; curTokenIndex--) {
-// WindowToken token = mWallpaperTokens.get(curTokenIndex);
-// for (int curWallpaperIndex = token.windows.size() - 1; curWallpaperIndex >= 0;
-// curWallpaperIndex--) {
-// WindowState wallpaper = token.windows.get(curWallpaperIndex);
-// if (wallpaper.mWallpaperVisible && !wallpaper.isDrawnLw()) {
-// // We've told this wallpaper to be visible, but it is not drawn yet
-// wallpaperGoodToGo = false;
-// if (mWallpaperDrawState != WALLPAPER_DRAW_TIMEOUT) {
-// // wait for this wallpaper until it is drawn or timeout
-// goodToGo = false;
-// }
-// if (mWallpaperDrawState == WALLPAPER_DRAW_NORMAL) {
-// mWallpaperDrawState = WALLPAPER_DRAW_PENDING;
-// mH.removeMessages(H.WALLPAPER_DRAW_PENDING_TIMEOUT);
-// mH.sendEmptyMessageDelayed(H.WALLPAPER_DRAW_PENDING_TIMEOUT,
-// WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION);
-// }
-// if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG,
-// "Wallpaper should be visible but has not been drawn yet. " +
-// "mWallpaperDrawState=" + mWallpaperDrawState);
-// break;
-// }
-// }
-// }
-// if (wallpaperGoodToGo) {
-// mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;
-// mH.removeMessages(H.WALLPAPER_DRAW_PENDING_TIMEOUT);
-// }
-// }
+
+ if (goodToGo && isWallpaperVisible(mWallpaperTarget)) {
+ boolean wallpaperGoodToGo = true;
+ for (int curTokenIndex = mWallpaperTokens.size() - 1;
+ curTokenIndex >= 0 && wallpaperGoodToGo; curTokenIndex--) {
+ WindowToken token = mWallpaperTokens.get(curTokenIndex);
+ for (int curWallpaperIndex = token.windows.size() - 1; curWallpaperIndex >= 0;
+ curWallpaperIndex--) {
+ WindowState wallpaper = token.windows.get(curWallpaperIndex);
+ if (wallpaper.mWallpaperVisible && !wallpaper.isDrawnLw()) {
+ // We've told this wallpaper to be visible, but it is not drawn yet
+ wallpaperGoodToGo = false;
+ if (mWallpaperDrawState != WALLPAPER_DRAW_TIMEOUT) {
+ // wait for this wallpaper until it is drawn or timeout
+ goodToGo = false;
+ }
+ if (mWallpaperDrawState == WALLPAPER_DRAW_NORMAL) {
+ mWallpaperDrawState = WALLPAPER_DRAW_PENDING;
+ mH.removeMessages(H.WALLPAPER_DRAW_PENDING_TIMEOUT);
+ mH.sendEmptyMessageDelayed(H.WALLPAPER_DRAW_PENDING_TIMEOUT,
+ WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION);
+ }
+ if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG,
+ "Wallpaper should be visible but has not been drawn yet. " +
+ "mWallpaperDrawState=" + mWallpaperDrawState);
+ break;
+ }
+ }
+ }
+ if (wallpaperGoodToGo) {
+ mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;
+ mH.removeMessages(H.WALLPAPER_DRAW_PENDING_TIMEOUT);
+ }
+ }
}
if (goodToGo) {
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO");
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index ac1b0f1..1a30cba 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1403,6 +1403,9 @@
if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
"SIZE " + width + "x" + height, null);
mSurfaceControl.setSize(width, height);
+ mSurfaceControl.setMatrix(
+ mDsDx * w.mHScale, mDtDx * w.mVScale,
+ mDsDy * w.mHScale, mDtDy * w.mVScale);
mAnimator.setPendingLayoutChanges(w.getDisplayId(),
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
if ((w.mAttrs.flags & LayoutParams.FLAG_DIM_BEHIND) != 0) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 45584cd..bb3085e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -280,15 +280,12 @@
long mLastMaximumTimeToLock = -1;
boolean mUserSetupComplete = false;
- final HashMap<ComponentName, ActiveAdmin> mAdminMap
- = new HashMap<ComponentName, ActiveAdmin>();
- final ArrayList<ActiveAdmin> mAdminList
- = new ArrayList<ActiveAdmin>();
- final ArrayList<ComponentName> mRemovingAdmins
- = new ArrayList<ComponentName>();
+ final HashMap<ComponentName, ActiveAdmin> mAdminMap = new HashMap<>();
+ final ArrayList<ActiveAdmin> mAdminList = new ArrayList<>();
+ final ArrayList<ComponentName> mRemovingAdmins = new ArrayList<>();
// This is the list of component allowed to start lock task mode.
- final List<String> mLockTaskPackages = new ArrayList<String>();
+ final List<String> mLockTaskPackages = new ArrayList<>();
ComponentName mRestrictionsProvider;
@@ -299,7 +296,7 @@
}
}
- final SparseArray<DevicePolicyData> mUserData = new SparseArray<DevicePolicyData>();
+ final SparseArray<DevicePolicyData> mUserData = new SparseArray<>();
Handler mHandler = new Handler();
@@ -1596,6 +1593,16 @@
validatePasswordOwnerLocked(policy);
syncDeviceCapabilitiesLocked(policy);
updateMaximumTimeToLockLocked(policy);
+ updateLockTaskPackagesLocked(policy, userHandle);
+ }
+
+ private void updateLockTaskPackagesLocked(DevicePolicyData policy, int userId) {
+ IActivityManager am = ActivityManagerNative.getDefault();
+ try {
+ am.updateLockTaskPackages(userId, policy.mLockTaskPackages.toArray(new String[0]));
+ } catch (RemoteException e) {
+ // Not gonna happen.
+ }
}
static void validateQualityConstant(int quality) {
@@ -5533,6 +5540,7 @@
// Store the settings persistently.
saveSettingsLocked(userHandle);
+ updateLockTaskPackagesLocked(policy, userHandle);
}
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index 235567c..4498b84 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -18,6 +18,7 @@
import android.app.usage.TimeSparseArray;
import android.app.usage.UsageStatsManager;
+import android.os.Build;
import android.util.AtomicFile;
import android.util.Slog;
@@ -35,7 +36,7 @@
* Provides an interface to query for UsageStat data from an XML database.
*/
class UsageStatsDatabase {
- private static final int CURRENT_VERSION = 2;
+ private static final int CURRENT_VERSION = 3;
private static final String TAG = "UsageStatsDatabase";
private static final boolean DEBUG = UsageStatsService.DEBUG;
@@ -47,6 +48,8 @@
private final TimeSparseArray<AtomicFile>[] mSortedStatFiles;
private final UnixCalendar mCal;
private final File mVersionFile;
+ private boolean mFirstUpdate;
+ private boolean mNewUpdate;
public UsageStatsDatabase(File dir) {
mIntervalDirs = new File[] {
@@ -73,7 +76,7 @@
}
}
- checkVersionLocked();
+ checkVersionAndBuildLocked();
indexFilesLocked();
// Delete files that are in the future.
@@ -194,10 +197,35 @@
}
}
- private void checkVersionLocked() {
+ /**
+ * Is this the first update to the system from L to M?
+ */
+ boolean isFirstUpdate() {
+ return mFirstUpdate;
+ }
+
+ /**
+ * Is this a system update since we started tracking build fingerprint in the version file?
+ */
+ boolean isNewUpdate() {
+ return mNewUpdate;
+ }
+
+ private void checkVersionAndBuildLocked() {
int version;
+ String buildFingerprint;
+ String currentFingerprint = getBuildFingerprint();
+ mFirstUpdate = true;
+ mNewUpdate = true;
try (BufferedReader reader = new BufferedReader(new FileReader(mVersionFile))) {
version = Integer.parseInt(reader.readLine());
+ buildFingerprint = reader.readLine();
+ if (buildFingerprint != null) {
+ mFirstUpdate = false;
+ }
+ if (currentFingerprint.equals(buildFingerprint)) {
+ mNewUpdate = false;
+ }
} catch (NumberFormatException | IOException e) {
version = 0;
}
@@ -205,9 +233,15 @@
if (version != CURRENT_VERSION) {
Slog.i(TAG, "Upgrading from version " + version + " to " + CURRENT_VERSION);
doUpgradeLocked(version);
+ }
+ if (version != CURRENT_VERSION || mNewUpdate) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(mVersionFile))) {
writer.write(Integer.toString(CURRENT_VERSION));
+ writer.write("\n");
+ writer.write(currentFingerprint);
+ writer.write("\n");
+ writer.flush();
} catch (IOException e) {
Slog.e(TAG, "Failed to write new version");
throw new RuntimeException(e);
@@ -215,6 +249,12 @@
}
}
+ private String getBuildFingerprint() {
+ return Build.VERSION.RELEASE + ";"
+ + Build.VERSION.CODENAME + ";"
+ + Build.VERSION.INCREMENTAL;
+ }
+
private void doUpgradeLocked(int thisVersion) {
if (thisVersion < 2) {
// Delete all files if we are version 0. This is a pre-release version,
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index f458dbc..cc0ab81 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -92,7 +92,7 @@
long mRealTimeSnapshot;
long mSystemTimeSnapshot;
- private static final long DEFAULT_APP_IDLE_THRESHOLD_MILLIS = 3L * 24 * 60 * 60 * 1000; //3 days
+ private static final long DEFAULT_APP_IDLE_THRESHOLD_MILLIS = 1L * 24 * 60 * 60 * 1000; // 1 day
private long mAppIdleDurationMillis;
private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index afe27c7..0a9481a 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -19,8 +19,11 @@
import android.app.usage.ConfigurationStats;
import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents;
+import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.SystemClock;
import android.content.Context;
@@ -60,6 +63,7 @@
private final UnixCalendar mDailyExpiryDate;
private final StatsUpdatedListener mListener;
private final String mLogPrefix;
+ private final int mUserId;
interface StatsUpdatedListener {
void onStatsUpdated();
@@ -73,6 +77,7 @@
mCurrentStats = new IntervalStats[UsageStatsManager.INTERVAL_COUNT];
mListener = listener;
mLogPrefix = "User[" + Integer.toString(userId) + "] ";
+ mUserId = userId;
}
void init(final long currentTimeMillis) {
@@ -128,6 +133,35 @@
stat.updateConfigurationStats(null, stat.lastTimeSaved);
}
+
+ if (mDatabase.isNewUpdate()) {
+ initializeDefaultsForApps(currentTimeMillis, mDatabase.isFirstUpdate());
+ }
+ }
+
+ /**
+ * If any of the apps don't have a last-used entry, add one now.
+ * @param currentTimeMillis the current time
+ * @param firstUpdate if it is the first update, touch all installed apps, otherwise only
+ * touch the system apps
+ */
+ private void initializeDefaultsForApps(long currentTimeMillis, boolean firstUpdate) {
+ PackageManager pm = mContext.getPackageManager();
+ List<PackageInfo> packages = pm.getInstalledPackages(0, mUserId);
+ final int packageCount = packages.size();
+ for (int i = 0; i < packageCount; i++) {
+ final PackageInfo pi = packages.get(i);
+ String packageName = pi.packageName;
+ if (pi.applicationInfo != null && (firstUpdate || pi.applicationInfo.isSystemApp())
+ && getLastPackageAccessTime(packageName) == -1) {
+ for (IntervalStats stats : mCurrentStats) {
+ stats.update(packageName, currentTimeMillis, Event.INTERACTION);
+ mStatsChanged = true;
+ }
+ }
+ }
+ // Persist the new OTA-related access stats.
+ persistActiveStats();
}
void onTimeChanged(long oldTime, long newTime) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 1aa0d0b..bca757b 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -145,7 +145,10 @@
}
public boolean hideSessionLocked(int callingPid, int callingUid) {
- return mActiveSession.hideLocked();
+ if (mActiveSession != null) {
+ return mActiveSession.hideLocked();
+ }
+ return false;
}
public boolean deliverNewSessionLocked(int callingPid, int callingUid, IBinder token,
@@ -165,6 +168,10 @@
Slog.w(TAG, "startVoiceActivity does not match active session");
return ActivityManager.START_CANCELED;
}
+ if (!mActiveSession.mShown) {
+ Slog.w(TAG, "startVoiceActivity not allowed on hidden session");
+ return ActivityManager.START_CANCELED;
+ }
intent = new Intent(intent);
intent.addCategory(Intent.CATEGORY_VOICE);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 73c7363..607df2d 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -18,6 +18,7 @@
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
+import android.app.AppOpsManager;
import android.app.AssistContent;
import android.app.IActivityManager;
import android.content.ClipData;
@@ -62,6 +63,7 @@
final int mCallingUid;
final IActivityManager mAm;
final IWindowManager mIWindowManager;
+ final AppOpsManager mAppOps;
final IBinder mPermissionOwner;
boolean mShown;
Bundle mShowArgs;
@@ -148,6 +150,7 @@
mAm = ActivityManagerNative.getDefault();
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
+ mAppOps = context.getSystemService(AppOpsManager.class);
IBinder permOwner = null;
try {
permOwner = mAm.newUriPermissionOwner("voicesession:"
@@ -159,7 +162,8 @@
mBindIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
mBindIntent.setComponent(mSessionComponentName);
mBound = mContext.bindServiceAsUser(mBindIntent, this,
- Context.BIND_AUTO_CREATE|Context.BIND_ALLOW_OOM_MANAGEMENT, new UserHandle(mUser));
+ Context.BIND_AUTO_CREATE|Context.BIND_WAIVE_PRIORITY
+ |Context.BIND_ALLOW_OOM_MANAGEMENT, new UserHandle(mUser));
if (mBound) {
try {
mIWindowManager.addWindowToken(mToken,
@@ -186,19 +190,31 @@
mShowFlags = flags;
mHaveAssistData = false;
if ((flags&VoiceInteractionService.START_WITH_ASSIST) != 0) {
- try {
- mAm.requestAssistContextExtras(ActivityManager.ASSIST_CONTEXT_FULL,
- mAssistReceiver);
- } catch (RemoteException e) {
+ if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ASSIST_STRUCTURE, mCallingUid,
+ mSessionComponentName.getPackageName()) == AppOpsManager.MODE_ALLOWED) {
+ try {
+ mAm.requestAssistContextExtras(ActivityManager.ASSIST_CONTEXT_FULL,
+ mAssistReceiver);
+ } catch (RemoteException e) {
+ }
+ } else {
+ mHaveAssistData = true;
+ mAssistData = null;
}
} else {
mAssistData = null;
}
mHaveScreenshot = false;
if ((flags&VoiceInteractionService.START_WITH_SCREENSHOT) != 0) {
- try {
- mIWindowManager.requestAssistScreenshot(mScreenshotReceiver);
- } catch (RemoteException e) {
+ if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ASSIST_SCREENSHOT, mCallingUid,
+ mSessionComponentName.getPackageName()) == AppOpsManager.MODE_ALLOWED) {
+ try {
+ mIWindowManager.requestAssistScreenshot(mScreenshotReceiver);
+ } catch (RemoteException e) {
+ }
+ } else {
+ mHaveScreenshot = true;
+ mScreenshot = null;
}
} else {
mScreenshot = null;
@@ -335,6 +351,12 @@
mUser);
} catch (RemoteException e) {
}
+ if (mSession != null) {
+ try {
+ mAm.finishVoiceTask(mSession);
+ } catch (RemoteException e) {
+ }
+ }
}
if (mFullyBound) {
mContext.unbindService(mFullConnection);
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index a204376..c8b6846 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -485,14 +485,19 @@
throw new UnsupportedOperationException();
}
- /**
- * @hide - to match hiding in superclass
- */
+ /** {@hide} */
@Override
public void movePackage(String packageName, IPackageMoveObserver observer, int flags) {
throw new UnsupportedOperationException();
}
+ /** {@hide} */
+ @Override
+ public void movePackageAndData(String packageName, String volumeUuid,
+ IPackageMoveObserver observer) {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public String getInstallerPackageName(String packageName) {
throw new UnsupportedOperationException();
diff --git a/tests/CameraPrewarmTest/Android.mk b/tests/CameraPrewarmTest/Android.mk
new file mode 100644
index 0000000..b6316f0
--- /dev/null
+++ b/tests/CameraPrewarmTest/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := CameraPrewarmTest
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/tests/CameraPrewarmTest/AndroidManifest.xml b/tests/CameraPrewarmTest/AndroidManifest.xml
new file mode 100644
index 0000000..eb40200
--- /dev/null
+++ b/tests/CameraPrewarmTest/AndroidManifest.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.test.cameraprewarm">
+ <application android:label="@string/activity_title">
+
+ <activity android:name=".CameraActivity"
+ android:theme="@android:style/Theme.NoTitleBar">
+ <intent-filter>
+ <action android:name="android.media.action.STILL_IMAGE_CAMERA_SECURE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.media.action.STILL_IMAGE_CAMERA" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name=".SecureCameraActivity"
+ android:theme="@android:style/Theme.NoTitleBar">
+ <intent-filter>
+ <action android:name="android.media.action.STILL_IMAGE_CAMERA_SECURE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <receiver android:name=".PrewarmReceiver" >
+ <intent-filter>
+ <action android:name="android.media.action.STILL_IMAGE_CAMERA_PREWARM" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.media.action.STILL_IMAGE_CAMERA_COOLDOWN" />
+ </intent-filter>
+ </receiver>
+
+ </application>
+</manifest>
diff --git a/tests/CameraPrewarmTest/res/layout/camera_activity.xml b/tests/CameraPrewarmTest/res/layout/camera_activity.xml
new file mode 100644
index 0000000..64437bc
--- /dev/null
+++ b/tests/CameraPrewarmTest/res/layout/camera_activity.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center">
+</LinearLayout>
+
diff --git a/tests/CameraPrewarmTest/res/values/strings.xml b/tests/CameraPrewarmTest/res/values/strings.xml
new file mode 100644
index 0000000..11f7ac7
--- /dev/null
+++ b/tests/CameraPrewarmTest/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources>
+ <string name="activity_title">Assistant</string>
+ <string name="search_label">Orilla Search Engine</string>
+</resources>
diff --git a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java
new file mode 100644
index 0000000..4d22234
--- /dev/null
+++ b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.google.android.test.cameraprewarm;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.WindowManager;
+
+import com.google.android.test.cameraprewarm.R;
+
+public class CameraActivity extends Activity {
+
+ public final static String TAG = "PrewarmTest";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.camera_activity);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+ Log.i(TAG, "Activity created");
+ }
+}
diff --git a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/PrewarmReceiver.java b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/PrewarmReceiver.java
new file mode 100644
index 0000000..d49f96d
--- /dev/null
+++ b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/PrewarmReceiver.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.google.android.test.cameraprewarm;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.MediaStore;
+import android.util.Log;
+
+public class PrewarmReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(MediaStore.ACTION_STILL_IMAGE_CAMERA_PREWARM)) {
+ Log.i(CameraActivity.TAG, "Prewarm received");
+ } else if (intent.getAction().equals(MediaStore.ACTION_STILL_IMAGE_CAMERA_COOLDOWN)){
+ Log.i(CameraActivity.TAG, "Cooldown received");
+ }
+ }
+}
diff --git a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java
new file mode 100644
index 0000000..530fe00
--- /dev/null
+++ b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.google.android.test.cameraprewarm;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.WindowManager;
+
+import com.google.android.test.cameraprewarm.R;
+
+public class SecureCameraActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.camera_activity);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ Log.i(CameraActivity.TAG, "Activity created");
+ }
+}
diff --git a/tests/VoiceInteraction/res/layout/test_interaction.xml b/tests/VoiceInteraction/res/layout/test_interaction.xml
index 8c8151d..6209bd08 100644
--- a/tests/VoiceInteraction/res/layout/test_interaction.xml
+++ b/tests/VoiceInteraction/res/layout/test_interaction.xml
@@ -34,32 +34,49 @@
android:textAppearance="?android:attr/textAppearanceMedium"
/>
- <Button android:id="@+id/complete"
- android:layout_width="wrap_content"
+ <LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
- android:text="@string/completeVoice"
+ android:orientation="horizontal">
+
+ <Button android:id="@+id/complete"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/completeVoice"
+ />
+
+ <Button android:id="@+id/abort"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/abortVoice"
+ />
+
+ <Button android:id="@+id/pick"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/pickVoice"
+ />
+
+ </LinearLayout>
+
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="16dp"
+ android:orientation="horizontal">
+
+ <Button android:id="@+id/cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/cancelVoice"
/>
- <Button android:id="@+id/pick"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
- android:text="@string/pickVoice"
+ <Button android:id="@+id/jump"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/jumpOut"
/>
- <Button android:id="@+id/abort"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
- android:text="@string/abortVoice"
- />
-
- <Button android:id="@+id/cancel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
- android:text="@string/cancelVoice"
- />
+ </LinearLayout>
</LinearLayout>
diff --git a/tests/VoiceInteraction/res/values/strings.xml b/tests/VoiceInteraction/res/values/strings.xml
index 942c931..6289929 100644
--- a/tests/VoiceInteraction/res/values/strings.xml
+++ b/tests/VoiceInteraction/res/values/strings.xml
@@ -25,5 +25,6 @@
<string name="completeVoice">Complete Voice</string>
<string name="pickVoice">Pick Voice</string>
<string name="cancelVoice">Cancel</string>
+ <string name="jumpOut">Jump out</string>
</resources>
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
index ec727c4..3c5c201 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
@@ -16,6 +16,7 @@
package com.android.test.voiceinteraction;
+import android.app.ActivityManager;
import android.app.AssistContent;
import android.app.AssistStructure;
import android.app.VoiceInteractor;
@@ -69,6 +70,8 @@
@Override
public void onCreate(Bundle args, int startFlags) {
super.onCreate(args);
+ ActivityManager am = getContext().getSystemService(ActivityManager.class);
+ am.setWatchHeapLimit(40*1024*1024);
}
@Override
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
index e195c30..9d24c59 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
@@ -19,6 +19,7 @@
import android.app.Activity;
import android.app.VoiceInteractor;
import android.content.ComponentName;
+import android.content.Intent;
import android.os.Bundle;
import android.service.voice.VoiceInteractionService;
import android.util.Log;
@@ -37,6 +38,7 @@
Button mAbortButton;
Button mCompleteButton;
Button mPickButton;
+ Button mJumpOutButton;
Button mCancelButton;
@Override
@@ -64,6 +66,8 @@
mCompleteButton.setOnClickListener(this);
mPickButton = (Button)findViewById(R.id.pick);
mPickButton.setOnClickListener(this);
+ mJumpOutButton = (Button)findViewById(R.id.jump);
+ mJumpOutButton.setOnClickListener(this);
mCancelButton = (Button)findViewById(R.id.cancel);
mCancelButton.setOnClickListener(this);
@@ -165,6 +169,13 @@
}
};
mInteractor.submitRequest(req);
+ } else if (v == mJumpOutButton) {
+ Log.i(TAG, "Jump out");
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+ intent.setComponent(new ComponentName(this, VoiceInteractionMain.class));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
} else if (v == mCancelButton && mCurrentRequest != null) {
Log.i(TAG, "Cancel request");
mCurrentRequest.cancel();
diff --git a/tools/layoutlib/rename_font/build_font.py b/tools/layoutlib/rename_font/build_font.py
index c747d92a..9713a4c 100755
--- a/tools/layoutlib/rename_font/build_font.py
+++ b/tools/layoutlib/rename_font/build_font.py
@@ -209,15 +209,18 @@
def get_version(string):
- # The string must begin with 'Version n.nn '
- # to extract n.nn, we return the second entry in the split strings.
string = string.strip()
- if not string.startswith('Version '):
- raise InvalidFontException('mal-formed font version')
- return sanitize(string.split()[1])
+ # The spec says that the version string should start with "Version ". But not
+ # all fonts do. So, we return the complete string if it doesn't start with
+ # the prefix, else we return the rest of the string after sanitizing it.
+ prefix = 'Version '
+ if string.startswith(prefix):
+ string = string[len(prefix):]
+ return sanitize(string)
def sanitize(string):
+ """ Remove non-standard chars. """
return re.sub(r'[^\w-]+', '', string)
if __name__ == '__main__':
diff --git a/tools/layoutlib/rename_font/build_font_single.py b/tools/layoutlib/rename_font/build_font_single.py
index 5f7dad9..4245cdc 100755
--- a/tools/layoutlib/rename_font/build_font_single.py
+++ b/tools/layoutlib/rename_font/build_font_single.py
@@ -193,15 +193,18 @@
def get_version(string):
- # The string must begin with 'Version n.nn '
- # to extract n.nn, we return the second entry in the split strings.
string = string.strip()
- if not string.startswith('Version '):
- raise InvalidFontException('mal-formed font version')
- return sanitize(string.split()[1])
+ # The spec says that the version string should start with "Version ". But not
+ # all fonts do. So, we return the complete string if it doesn't start with
+ # the prefix, else we return the rest of the string after sanitizing it.
+ prefix = 'Version '
+ if string.startswith(prefix):
+ string = string[len(prefix):]
+ return sanitize(string)
def sanitize(string):
+ """ Remove non-standard chars. """
return re.sub(r'[^\w-]+', '', string)
if __name__ == '__main__':