diff options
131 files changed, 3534 insertions, 1677 deletions
diff --git a/api/current.txt b/api/current.txt index d79ccbe48107..bf55b7c3e408 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1696,6 +1696,8 @@ package android { 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 @@ package android.app { 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 @@ package android.app { 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 @@ package android.app { 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 @@ package android.app.admin { 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 @@ package android.content { 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 @@ package android.content { 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 @@ package android.provider { 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 @@ package android.provider { 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 @@ package android.renderscript { 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 @@ package android.service.voice { 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 @@ package android.view.accessibility { 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 @@ package android.view.accessibility { 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 e57eee5ca95a..1152de5fe49a 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1773,6 +1773,8 @@ package android { 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 @@ package android.app { 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 @@ package android.app { 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 @@ package android.app { 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 @@ package android.app.admin { 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 @@ package android.content { 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 @@ package android.content { 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 @@ package android.provider { 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 @@ package android.provider { 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 @@ package android.renderscript { 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 @@ package android.service.voice { 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 @@ package android.view.accessibility { 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 @@ package android.view.accessibility { 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 @@ package android.webkit { 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 0a53371bae62..908d46e9c409 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 @@ public class Am extends BaseCommand { 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 89dd0792cbe3..b43c4626817f 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.FeatureInfo; 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.os.UserManager; 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 @@ public final class Pm { return runForceDexOpt(); } + if ("move".equals(op)) { + return runMove(); + } + try { if (args.length == 1) { if (args[0].equalsIgnoreCase("-l")) { @@ -1278,6 +1283,51 @@ public final class Pm { } } + 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 @@ public final class Pm { } 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 @@ public final class Pm { 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 1738ade34080..1ba68dfd7061 100644 --- a/core/java/android/animation/TimeAnimator.java +++ b/core/java/android/animation/TimeAnimator.java @@ -51,6 +51,7 @@ public class TimeAnimator extends ValueAnimator { 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 85dc8320803f..2386007b8ac2 100644 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -20,6 +20,7 @@ import android.annotation.CallSuper; 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 @@ import java.util.HashMap; */ @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 @@ public class ValueAnimator extends Animator { * 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 @@ public class ValueAnimator extends Animator { * 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 @@ public class ValueAnimator extends Animator { * * @return The length of the animation, in milliseconds. */ + @Override public long getDuration() { return mUnscaledDuration; } @@ -608,6 +631,7 @@ public class ValueAnimator extends Animator { 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 @@ public class ValueAnimator extends Animator { * @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 @@ public class ValueAnimator extends Animator { private final Choreographer mChoreographer; private boolean mAnimationScheduled; + private long mLastFrameTime; private AnimationHandler() { mChoreographer = Choreographer.getInstance(); @@ -679,7 +704,9 @@ public class ValueAnimator extends Animator { 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 @@ public class ValueAnimator extends Animator { } } } + // 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 @@ public class ValueAnimator extends Animator { 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 @@ public class ValueAnimator extends Animator { } } - // 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 @@ public class ValueAnimator extends Animator { * * @return the number of milliseconds to delay running the animation */ + @Override public long getStartDelay() { return mUnscaledStartDelay; } @@ -778,6 +828,7 @@ public class ValueAnimator extends Animator { * @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 @@ public class ValueAnimator extends Animator { 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 @@ public class ValueAnimator extends Animator { } 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 @@ public class ValueAnimator extends Animator { } /** + * 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 @@ public class ValueAnimator extends Animator { 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 @@ public class ValueAnimator extends Animator { mStartTime = frameTime - seekTime; mSeekFraction = -1; } + mStartTimeCommitted = false; // allow start time to be compensated for jank } if (mPaused) { if (mPauseTime < 0) { @@ -1345,6 +1416,7 @@ public class ValueAnimator extends Animator { 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 d143f8b5d65b..8f125d7ba9fa 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -2682,6 +2682,47 @@ public class ActivityManager { } /** + * 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 1484af8aa691..f63d13cec572 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -2427,8 +2427,10 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM 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 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM 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 @@ class ActivityManagerProxy implements IActivityManager } @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 @@ class ActivityManagerProxy implements IActivityManager 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 4bd23320b516..381c20c136f4 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -208,8 +208,12 @@ public class AppOpsManager { 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 @@ public class AppOpsManager { OP_PROJECT_MEDIA, OP_ACTIVATE_VPN, OP_WRITE_WALLPAPER, + OP_ASSIST_STRUCTURE, + OP_ASSIST_SCREENSHOT, }; /** @@ -344,6 +350,8 @@ public class AppOpsManager { null, OPSTR_ACTIVATE_VPN, null, + null, + null, }; /** @@ -400,6 +408,8 @@ public class AppOpsManager { "PROJECT_MEDIA", "ACTIVATE_VPN", "WRITE_WALLPAPER", + "ASSIST_STRUCTURE", + "ASSIST_SCREENSHOT" }; /** @@ -456,6 +466,8 @@ public class AppOpsManager { 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 @@ public class AppOpsManager { null, //PROJECT_MEDIA UserManager.DISALLOW_CONFIG_VPN, // ACTIVATE_VPN UserManager.DISALLOW_WALLPAPER, // WRITE_WALLPAPER + null, // ASSIST_STRUCTURE + null, // ASSIST_SCREENSHOT }; /** @@ -569,6 +583,8 @@ public class AppOpsManager { false, //PROJECT_MEDIA false, //ACTIVATE_VPN false, //WALLPAPER + false, //ASSIST_STRUCTURE + false, //ASSIST_SCREENSHOT }; /** @@ -624,6 +640,8 @@ public class AppOpsManager { 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 @@ public class AppOpsManager { 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 6ec54574651e..10dcd85d6afa 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -62,19 +62,19 @@ import android.graphics.drawable.Drawable; 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 @@ final class ApplicationPackageManager extends PackageManager { 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 d794aa3a37de..4a1d6ff8fe2c 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -482,11 +482,13 @@ public interface IActivityManager extends IInterface { 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 @@ public interface IActivityManager extends IInterface { 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 da7bb05f4862..7acf5f07edf6 100644 --- a/core/java/android/app/VoiceInteractor.java +++ b/core/java/android/app/VoiceInteractor.java @@ -103,9 +103,9 @@ public class VoiceInteractor { 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 class VoiceInteractor { */ public static final class Option implements Parcelable { final CharSequence mLabel; + final int mIndex; ArrayList<CharSequence> mSynonyms; Bundle mExtras; @@ -308,6 +309,21 @@ public class VoiceInteractor { */ 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 @@ public class VoiceInteractor { 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 @@ public class VoiceInteractor { Option(Parcel in) { mLabel = in.readCharSequence(); + mIndex = in.readInt(); mSynonyms = in.readCharSequenceList(); mExtras = in.readBundle(); } @@ -368,6 +393,7 @@ public class VoiceInteractor { @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 e9175228bf5a..68f4707c5a50 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -502,6 +502,20 @@ public class DevicePolicyManager { */ 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 ca6dc6968ae3..1131ff9faeda 100644 --- a/core/java/android/app/backup/BackupTransport.java +++ b/core/java/android/app/backup/BackupTransport.java @@ -464,7 +464,7 @@ public class BackupTransport { * 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 fd69d2043d15..1a64da666fb0 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 514802e9aebf..eea47b77fc93 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2908,6 +2908,24 @@ public class Intent implements Parcelable, Cloneable { /** {@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 8567c018d820..5bdb7bbcf60e 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -459,6 +459,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public int largestWidthLimitDp = 0; /** {@hide} */ + public String volumeUuid; + /** {@hide} */ public String scanSourceDir; /** {@hide} */ public String scanPublicSourceDir; @@ -726,6 +728,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 ba62cd629ebb..154ff85b0113 100644 --- a/core/java/android/content/pm/IPackageInstaller.aidl +++ b/core/java/android/content/pm/IPackageInstaller.aidl @@ -44,7 +44,8 @@ interface IPackageInstaller { 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 fb7c96d976b4..eed0df5338b2 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -269,6 +269,12 @@ interface IPackageManager { 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 @@ interface IPackageManager { 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 80efd0b22197..b7ee82d41693 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -423,7 +423,7 @@ public class PackageInstaller { */ 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 class PackageInstaller { public Uri referrerUri; /** {@hide} */ public String abiOverride; + /** {@hide} */ + public String volumeUuid; /** * Construct parameters for a new package install session. @@ -911,6 +913,7 @@ public class PackageInstaller { originatingUri = source.readParcelable(null); referrerUri = source.readParcelable(null); abiOverride = source.readString(); + volumeUuid = source.readString(); } /** @@ -1008,6 +1011,7 @@ public class PackageInstaller { pw.printPair("originatingUri", originatingUri); pw.printPair("referrerUri", referrerUri); pw.printPair("abiOverride", abiOverride); + pw.printPair("volumeUuid", volumeUuid); pw.println(); } @@ -1028,6 +1032,7 @@ public class PackageInstaller { 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 46d6ffb33048..4c99d09d81a2 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -43,7 +43,9 @@ import android.os.Bundle; 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 abstract class PackageManager { 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 @@ public abstract class PackageManager { * * @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 f4ad4345ed2d..c1e6a4d0397b 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -604,7 +604,7 @@ public class PackageParser { 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 @@ public class PackageParser { } /* 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 b5eeb3037e86..bc6d4cec2b04 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -1284,14 +1284,12 @@ public final class Configuration implements Parcelable, Comparable<Configuration /** * 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 299eb7eb70b5..334d18018c9f 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -117,6 +117,9 @@ public class Resources { 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 @@ public class Resources { } } - // 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 @@ public class Resources { 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 @@ public class Resources { } } 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,46 +2621,47 @@ public class Resources { 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 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) { + return entry.newDrawable(this); + } + } + return null; + } + private ConstantState getConstantStateLocked( LongSparseArray<WeakReference<ConstantState>> drawableCache, long key) { final WeakReference<ConstantState> wr = drawableCache.get(key); - if (wr != null) { // we have the key + if (wr != null) { final ConstantState entry = wr.get(); 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 + } else { + // Our entry has been purged. drawableCache.delete(key); } } return null; } - private Drawable getCachedDrawableLocked( - LongSparseArray<WeakReference<ConstantState>> drawableCache, long key) { - final ConstantState entry = getConstantStateLocked(drawableCache, key); - if (entry != null) { - return entry.newDrawable(this); - } - return null; - } - @Nullable ColorStateList loadColorStateList(TypedValue value, int id, Theme theme) throws NotFoundException { diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 1503bf5d8c93..f28c96e8d85a 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -2354,8 +2354,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <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 7f901c83dac0..85c574aa136f 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -852,8 +852,8 @@ public abstract class CameraMetadata<TKey> { /** * <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 abstract class CameraMetadata<TKey> { 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 abstract class CameraMetadata<TKey> { 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 @@ public abstract class CameraMetadata<TKey> { /** * <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 abstract class CameraMetadata<TKey> { 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 @@ public abstract class CameraMetadata<TKey> { /** * <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 @@ public abstract class CameraMetadata<TKey> { /** * <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/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index 0a4ed396ad53..7aa9787a73fa 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -18,6 +18,7 @@ package android.hardware.camera2.params; import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.utils.HashCodeHelpers; import android.util.Log; import android.view.Surface; import android.os.Parcel; @@ -159,6 +160,35 @@ public final class OutputConfiguration implements Parcelable { mSurface.writeToParcel(dest, flags); } + /** + * Check if this {@link OutputConfiguration} is equal to another {@link OutputConfiguration}. + * + * <p>Two output configurations are only equal if and only if the underlying surface and + * all other configuration parameters are equal. </p> + * + * @return {@code true} if the objects were equal, {@code false} otherwise + */ + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } else if (this == obj) { + return true; + } else if (obj instanceof OutputConfiguration) { + final OutputConfiguration other = (OutputConfiguration) obj; + return (mSurface == other.mSurface && mRotation == other.mRotation); + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return HashCodeHelpers.hashCode(mSurface.hashCode(), mRotation); + } + private static final String TAG = "OutputConfiguration"; private final Surface mSurface; private final int mRotation; diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 486f9b4b798a..5a5d8b7b3499 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -43,6 +43,7 @@ import java.util.ArrayList; 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 @@ public class StorageManager { 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 @@ public class StorageManager { } /** {@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 c8c2f65adece..2dc036191fb3 100644 --- a/core/java/android/os/storage/VolumeInfo.java +++ b/core/java/android/os/storage/VolumeInfo.java @@ -44,6 +44,7 @@ import java.io.File; * @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 724d76d8bcbd..25a35e1358e7 100644 --- a/core/java/android/provider/AlarmClock.java +++ b/core/java/android/provider/AlarmClock.java @@ -43,8 +43,14 @@ public final class AlarmClock { * 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 final class AlarmClock { 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 final class AlarmClock { 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 5afbd6dfc576..7565654b7ae7 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -226,6 +226,35 @@ public final class MediaStore { 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 e94a31222ed2..5e2accd8fc25 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -171,6 +171,10 @@ public final class KeymasterDefs { 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 4c31f8013e4a..20d7079d1557 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -520,6 +520,10 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { mCallbacks, true); } + public Context getContext() { + return mContext; + } + Request newRequest(IVoiceInteractorCallback callback) { synchronized (this) { Request req = new Request(callback, this); @@ -832,6 +836,12 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { 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 f4dff9b8f854..a99bdf5e0c6b 100644 --- a/core/java/android/text/Hyphenator.java +++ b/core/java/android/text/Hyphenator.java @@ -30,7 +30,7 @@ import java.util.Locale; * * @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 @@ import java.util.Locale; } 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 @@ import java.util.Locale; 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 c8149d9346f8..79a8489ca13b 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -71,7 +71,12 @@ import java.io.PrintWriter; */ 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 @@ public final class Choreographer { 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 final class Choreographer { 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 @@ public final class Choreographer { 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 @@ public final class Choreographer { } 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 @@ public final class Choreographer { if (!mFrameScheduled) { mFrameScheduled = true; if (USE_VSYNC) { - if (DEBUG) { + if (DEBUG_FRAMES) { Log.d(TAG, "Scheduling next frame on vsync."); } @@ -509,7 +527,7 @@ public final class Choreographer { } 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 @@ public final class Choreographer { 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 @@ public final class Choreographer { + "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 @@ public final class Choreographer { } 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 @@ public final class Choreographer { 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 @@ public final class Choreographer { // 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 384bd2cf1f65..b6f1e3b85513 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -8400,7 +8400,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @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 ec527d5dffd7..0736ed891593 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -266,7 +266,8 @@ public class AccessibilityNodeInfo implements Parcelable { * 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 @@ public class AccessibilityNodeInfo implements Parcelable { * 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 @@ public class AccessibilityNodeInfo implements Parcelable { * 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 @@ public class AccessibilityNodeInfo implements Parcelable { * 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 @@ public class AccessibilityNodeInfo implements Parcelable { * 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 @@ public class AccessibilityNodeInfo implements Parcelable { * 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 @@ public class AccessibilityNodeInfo implements Parcelable { * 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 @@ public class AccessibilityNodeInfo implements Parcelable { // 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 @@ public class AccessibilityNodeInfo implements Parcelable { } if (mActions == null) { - mActions = new ArrayList<AccessibilityAction>(); + mActions = new ArrayList<>(); } mActions.remove(action); @@ -3411,9 +3455,24 @@ public class AccessibilityNodeInfo implements Parcelable { * @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); + + /** + * 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<AccessibilityAction>(); + private static final ArraySet<AccessibilityAction> sStandardActions = new ArraySet<>(); static { sStandardActions.add(ACTION_FOCUS); sStandardActions.add(ACTION_CLEAR_FOCUS); @@ -3438,6 +3497,7 @@ public class AccessibilityNodeInfo implements Parcelable { 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 @@ public class AccessibilityNodeInfo implements Parcelable { 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 @@ public class AccessibilityNodeInfo implements Parcelable { 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 aa77d5ea170b..7ab5aaa252ce 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -40,6 +40,7 @@ import android.util.Log; 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 @@ public class WebView extends AbsoluteLayout 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 fa2ce1b22c6a..d5787de65ec5 100644 --- a/core/java/android/webkit/WebViewProvider.java +++ b/core/java/android/webkit/WebViewProvider.java @@ -32,6 +32,7 @@ import android.print.PrintDocumentAdapter; 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 @@ public interface WebViewProvider { 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 168066a30fe4..e7b6238c91a6 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -61,6 +61,7 @@ import android.view.ViewTreeObserver; 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 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te 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 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te 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 c6e3dc8632e7..be0ca71907df 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -21,6 +21,7 @@ import android.content.Context; 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.ViewDebug; 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 class GridView extends AbsListView { } 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 @@ public class GridView extends AbsListView { 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 @@ public class GridView extends AbsListView { 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 ee2c0554518e..def5929e33ca 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.ViewGroup; 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 @@ public class ListView extends AbsListView { 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 50d701aac1d0..16353e8a3ca3 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -316,7 +316,7 @@ public class ProgressBar extends View { 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 @@ public class ProgressBar extends View { 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 @@ public class ProgressBar extends View { 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 143dea4c4df2..52e17289cd6a 100644 --- a/core/java/android/widget/RadialTimePickerView.java +++ b/core/java/android/widget/RadialTimePickerView.java @@ -79,8 +79,10 @@ public class RadialTimePickerView extends View { // 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 @@ public class RadialTimePickerView extends View { 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 @@ public class RadialTimePickerView extends View { 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 @@ public class RadialTimePickerView extends View { 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 @@ public class RadialTimePickerView extends View { * @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 @@ public class RadialTimePickerView extends View { 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 @@ public class RadialTimePickerView extends View { 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 @@ public class RadialTimePickerView extends View { 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 @@ public class RadialTimePickerView extends View { initData(); invalidate(); + mTouchHelper.invalidateRoot(); } public void showMinutes(boolean animate) { @@ -587,6 +597,7 @@ public class RadialTimePickerView extends View { initData(); invalidate(); + mTouchHelper.invalidateRoot(); } private void initHoursAndMinutesText() { @@ -608,7 +619,7 @@ public class RadialTimePickerView extends View { mInnerTextHours = mHours12Texts; } - mOuterTextMinutes = mMinutesTexts; + mMinutesText = mMinutesTexts; final int hoursAlpha = mShowHours ? ALPHA_OPAQUE : ALPHA_TRANSPARENT; mAlpha[HOURS].setValue(hoursAlpha); @@ -627,9 +638,9 @@ public class RadialTimePickerView extends View { 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 @@ public class RadialTimePickerView extends View { 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 @@ public class RadialTimePickerView extends View { 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 @@ public class RadialTimePickerView extends View { 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 @@ public class RadialTimePickerView extends View { // 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 @@ public class RadialTimePickerView extends View { } // 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 @@ public class RadialTimePickerView extends View { } 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 - if (mIs24HourMode && mShowHours) { - if (hypotenuse >= mMinHypotenuseForInnerNumber - && hypotenuse <= mHalfwayHypotenusePoint) { - mIsOnInnerCircle = true; - } else if ((hypotenuse <= mMaxHypotenuseForOuterNumber || !constrainOutside) - && hypotenuse >= mHalfwayHypotenusePoint) { - mIsOnInnerCircle = false; - } else { - return -1; - } + // 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 { - 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; - } + return degrees; } + } - 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; - } + private boolean getInnerCircleFromXY(float x, float y) { + if (mIs24HourMode && mShowHours) { + final double dX = x - mXCenter; + final double dY = y - mYCenter; + final double distFromCenter = Math.sqrt(dX * dX + dY * dY); + return distFromCenter <= mHalfwayDist; } - return degrees; + return false; } boolean mChangedDuringTouch = false; @@ -987,34 +986,28 @@ public class RadialTimePickerView extends View { 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 @@ public class RadialTimePickerView extends View { @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 @@ public class RadialTimePickerView extends View { // 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 @@ public class RadialTimePickerView extends View { 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 @@ public class RadialTimePickerView extends View { } } 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 @@ public class RadialTimePickerView extends View { 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 @@ public class RadialTimePickerView extends View { 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 7e70b0c26ad2..0ce501ec6daa 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 class DumpHeapActivity extends Activity { 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 @@ public class DumpHeapActivity extends Activity { 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 @@ public class DumpHeapActivity extends Activity { @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 @@ public class DumpHeapActivity extends Activity { 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 1f0bb7676ce2..4efefa943947 100644 --- a/core/java/com/android/internal/app/LocalePicker.java +++ b/core/java/com/android/internal/app/LocalePicker.java @@ -246,9 +246,8 @@ public class LocalePicker extends ListFragment { 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 7bdb4be2b35c..255f1fddc6c7 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; 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 @@ public class PackageHelper { /** * 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 @@ public class PackageHelper { } 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 @@ public class PackageHelper { 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 @@ public class PackageHelper { 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 49d565d99fd6..70f7b72a4f57 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.ErrnoException; 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 @@ public class ZygoteInit { 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 @@ public class ZygoteInit { } } + 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 c15390450bce..7d56e9e53b1c 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 @@ public class ImageUtils { && 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 000000000000..6ac0d89c74d2 --- /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 037fd660fd82..19d9e292f9fb 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 @@ import java.io.IOException; 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 @@ public class SystemBackupAgent extends BackupAgentHelper { 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 class SystemBackupAgent extends BackupAgentHelper { 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 873b516117ea..4906f59572e0 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -577,7 +577,8 @@ public: 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 Binary files differdeleted file mode 100644 index 2fa6d7e7691d..000000000000 --- a/core/res/res/drawable-hdpi/list_divider_mtrl_alpha.9.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index 070bdbfdbce6..000000000000 --- a/core/res/res/drawable-mdpi/list_divider_mtrl_alpha.9.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index 0d2836d86824..000000000000 --- a/core/res/res/drawable-xhdpi/list_divider_mtrl_alpha.9.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index b8ac46d17e01..000000000000 --- a/core/res/res/drawable-xxhdpi/list_divider_mtrl_alpha.9.png +++ /dev/null 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 000000000000..066a8a73644b --- /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 babb6461f2e1..658a59d57330 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 7e963954de5d..aaf252a8e974 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 f1707d2acb84..24d17a4b9c98 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 4d9093295556..7dc3ff79d273 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 220d5e7a32ca..b4ba3162a785 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 42b5d5d05df2..5c3b90b1b9d5 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 37527e947971..f3a7647c827c 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/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index dbe2f3fa5cf2..f4df14e1bd8d 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -974,7 +974,7 @@ public class VectorDrawable extends Drawable { if (vPath.isClipPath()) { mRenderPath.addPath(path, mFinalPathMatrix); - canvas.clipPath(mRenderPath, Region.Op.REPLACE); + canvas.clipPath(mRenderPath); } else { VFullPath fullPath = (VFullPath) vPath; if (fullPath.mTrimPathStart != 0.0f || fullPath.mTrimPathEnd != 1.0f) { diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java index 0bd1dbda4a88..ed690de12ec4 100644 --- a/keystore/java/android/security/AndroidKeyStore.java +++ b/keystore/java/android/security/AndroidKeyStore.java @@ -466,74 +466,65 @@ public class AndroidKeyStore extends KeyStoreSpi { 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())); - } - if (params.getPadding() != null) { - args.addInt(KeymasterDefs.KM_TAG_PADDING, - KeyStoreKeyConstraints.Padding.toKeymaster(params.getPadding())); - } - if (params.getMaxUsesPerBoot() != null) { - args.addInt(KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT, params.getMaxUsesPerBoot()); + for (int keymasterBlockMode : + KeyStoreKeyConstraints.BlockMode.allToKeymaster(params.getBlockModes())) { + args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockMode); } - if (params.getMinSecondsBetweenOperations() != null) { - args.addInt(KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS, - params.getMinSecondsBetweenOperations()); + for (int keymasterPadding : + KeyStoreKeyConstraints.Padding.allToKeymaster(params.getPaddings())) { + args.addInt(KeymasterDefs.KM_TAG_PADDING, keymasterPadding); } - 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 @@ public class AndroidKeyStore extends KeyStoreSpi { // 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 705838350f80..0e490cd7cadb 100644 --- a/keystore/java/android/security/KeyGeneratorSpec.java +++ b/keystore/java/android/security/KeyGeneratorSpec.java @@ -19,12 +19,8 @@ package android.security; 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 @@ import javax.crypto.SecretKey; * {@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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { 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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { 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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { 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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * 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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { /** * 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. - */ - public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() { - return mPadding; - } - - /** - * 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 - */ - public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() { - return mBlockMode; - } - - /** - * Gets the minimum number of seconds that must expire since the most recent use of the key - * before it can be used again. - * - * @return number of seconds or {@code null} if there is no restriction on how frequently a key - * can be used. - * - * @hide + * Gets the set of padding schemes to which the key is restricted. */ - public Integer getMinSecondsBetweenOperations() { - return mMinSecondsBetweenOperations; + public @KeyStoreKeyConstraints.PaddingEnum int getPaddings() { + return mPaddings; } /** - * 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 + * Gets the set of block modes to which the key is restricted. */ - public Integer getMaxUsesPerBoot() { - return mMaxUsesPerBoot; + public @KeyStoreKeyConstraints.BlockModeEnum int getBlockModes() { + return mBlockModes; } /** - * 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. + * 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. * - * @hide + * @return user authenticators or {@code 0} if the key can be used without user authentication. */ - 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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { 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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * <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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * @see #setKeyValidityStart(Date) * @see #setKeyValidityForConsumptionEnd(Date) * @see #setKeyValidityForOriginationEnd(Date) - * - * @hide */ public Builder setKeyValidityEnd(Date endDate) { setKeyValidityForOriginationEnd(endDate); @@ -366,8 +311,6 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * <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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * <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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { } /** - * 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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { } /** - * 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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * 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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * @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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * @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 dd62e9a76c11..52b7097b77c7 100644 --- a/keystore/java/android/security/KeyPairGeneratorSpec.java +++ b/keystore/java/android/security/KeyPairGeneratorSpec.java @@ -24,10 +24,7 @@ import java.security.NoSuchAlgorithmException; 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 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { 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 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { 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 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { 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 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { 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 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { 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 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * 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. - * - * @hide - */ - public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() { - return mDigest; - } - - /** - * Gets the padding scheme to which the key is restricted. - * - * @return padding scheme or {@code null} if the padding scheme is not restricted. - * - * @hide - */ - public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() { - return mPadding; - } - - /** - * 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 digests to which the key is restricted. * * @hide */ - public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() { - return mBlockMode; + public @KeyStoreKeyConstraints.DigestEnum int getDigests() { + return mDigests; } /** - * Gets the minimum number of seconds that must expire since the most recent use of the private - * key before it can be used again. - * - * <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. + * Gets the set of padding schemes to which the key is restricted. * * @hide */ - public Integer getMinSecondsBetweenOperations() { - return mMinSecondsBetweenOperations; + public @KeyStoreKeyConstraints.PaddingEnum int getPaddings() { + return mPaddings; } /** - * 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. + * Gets the set of block modes to which the key is restricted. * * @hide */ - public Integer getMaxUsesPerBoot() { - return mMaxUsesPerBoot; + public @KeyStoreKeyConstraints.BlockModeEnum int getBlockModes() { + return mBlockModes; } /** - * 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. + * 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 user authenticators or empty set if the key can be used without user authentication. + * @return user authenticators or {@code 0} 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 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * <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 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { 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 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { } /** - * 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 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { } /** - * 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 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * * @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 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * <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 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * @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 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { 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 75034d165ce3..7137a9abbbe2 100644 --- a/keystore/java/android/security/KeyStoreKeyConstraints.java +++ b/keystore/java/android/security/KeyStoreKeyConstraints.java @@ -21,12 +21,8 @@ import android.security.keymaster.KeymasterDefs; 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 @@ public abstract class KeyStoreKeyConstraints { 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 abstract class KeyStoreKeyConstraints { 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 @@ public abstract class KeyStoreKeyConstraints { /** * @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 @@ public abstract class KeyStoreKeyConstraints { } @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 @@ public abstract class KeyStoreKeyConstraints { /** * 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 @@ public abstract class KeyStoreKeyConstraints { 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 @@ public abstract class KeyStoreKeyConstraints { 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 @@ public abstract class KeyStoreKeyConstraints { switch (padding) { case NONE: return "NONE"; - case ZERO: - return "ZERO"; case PKCS7: return "PKCS#7"; default: @@ -329,10 +301,33 @@ public abstract class KeyStoreKeyConstraints { 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 @@ public abstract class KeyStoreKeyConstraints { /** * 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 @@ public abstract class KeyStoreKeyConstraints { /** * @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 @@ public abstract class KeyStoreKeyConstraints { /** * @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 @@ public abstract class KeyStoreKeyConstraints { } @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 @@ public abstract class KeyStoreKeyConstraints { 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 @@ public abstract class KeyStoreKeyConstraints { /** * @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 @@ public abstract class KeyStoreKeyConstraints { } @Retention(RetentionPolicy.SOURCE) - @IntDef({UserAuthenticator.LOCK_SCREEN}) + @IntDef(flag = true, + value = {UserAuthenticator.LOCK_SCREEN}) public @interface UserAuthenticatorEnum {} /** @@ -535,7 +588,7 @@ public abstract class KeyStoreKeyConstraints { 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 abstract class KeyStoreKeyConstraints { 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 abstract class KeyStoreKeyConstraints { */ 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 @@ public abstract class KeyStoreKeyConstraints { /** * @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 @@ public abstract class KeyStoreKeyConstraints { /** * @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 @@ public abstract class KeyStoreKeyConstraints { } } } + + 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 48b6d066512f..abce32d558aa 100644 --- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java @@ -109,32 +109,20 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { } 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 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { // 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 e5e5accc80f3..256d9b32443a 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 @@ public class KeyStoreKeySpec implements KeySpec { 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 @@ public class KeyStoreKeySpec implements KeySpec { 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 @@ public class KeyStoreKeySpec implements KeySpec { } /** - * Gets the key's size in bits. + * Gets the size of the key in bits. */ public int getKeySize() { return mKeySize; @@ -152,78 +140,53 @@ public class KeyStoreKeySpec implements KeySpec { } /** - * 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. - */ - public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() { - return mBlockMode; - } - - /** - * 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. - */ - public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() { - return mPadding; - } - - /** - * 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 block modes with which the key can be used. */ - public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() { - return mDigest; + 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. - * - * @return number of seconds or {@code null} if there is no restriction on how frequently a key - * can be used. + * Gets the set of padding modes with which the key can be used. */ - public Integer getMinSecondsBetweenOperations() { - return mMinSecondsBetweenOperations; + public @KeyStoreKeyConstraints.PaddingEnum int getPaddings() { + return mPaddings; } /** - * 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 digest algorithms with which the key can be used. */ - public Integer getMaxUsesPerBoot() { - return mMaxUsesPerBoot; + public @KeyStoreKeyConstraints.DigestEnum int getDigests() { + return mDigests; } /** - * 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. + * 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 user authenticators or empty set if the key can be used without user authentication. + * @return user authenticators or {@code 0} if the key can be used without user authentication. */ - public Set<Integer> getUserAuthenticators() { - return new HashSet<Integer>(mUserAuthenticators); + public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getUserAuthenticators() { + return 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()}. + * 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 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 88bd6b400094..0b2f9b69b2f1 100644 --- a/keystore/java/android/security/KeyStoreParameter.java +++ b/keystore/java/android/security/KeyStoreParameter.java @@ -18,12 +18,10 @@ package android.security; 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 @@ public final class KeyStoreParameter implements ProtectionParameter { 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 @@ public final class KeyStoreParameter implements ProtectionParameter { 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 @@ public final class KeyStoreParameter implements ProtectionParameter { } /** - * 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. * - * @hide - */ - public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() { - return mPadding; - } - - /** - * Gets the digest to which the key is restricted when generating signatures or Message - * Authentication Codes (MACs). - * - * @return digest or {@code null} if the digest is not restricted. + * @see #isDigestsSpecified() * * @hide */ - public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() { - return mDigest; - } - - /** - * 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 - */ - public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() { - return mBlockMode; + public @KeyStoreKeyConstraints.DigestEnum int getDigests() { + if (mDigests == null) { + throw new IllegalStateException("Digests not specified"); + } + 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. + * Returns {@code true} if digest restrictions have been specified. * - * @return number of seconds or {@code null} if there is no restriction on how frequently a key - * can be used. + * @see #getDigests() * * @hide */ - public Integer getMinSecondsBetweenOperations() { - return mMinSecondsBetweenOperations; + public boolean isDigestsSpecified() { + return mDigests != null; } /** - * Gets the number of times the key can be used without rebooting the device. + * Gets the set of block modes to which the key is restricted. * - * @return maximum number of times or {@code null} if there is no restriction. * @hide */ - public Integer getMaxUsesPerBoot() { - return mMaxUsesPerBoot; + public @KeyStoreKeyConstraints.BlockModeEnum int getBlockModes() { + return mBlockModes; } /** - * 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. + * 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 user authenticators or empty set if the key can be used without user authentication. + * @return user authenticators or {@code 0} 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 @@ public final class KeyStoreParameter implements ProtectionParameter { 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 @@ public final class KeyStoreParameter implements ProtectionParameter { } /** - * 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 @@ public final class KeyStoreParameter implements ProtectionParameter { } /** - * 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. - * - * <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. + * 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. * - * @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 @@ public final class KeyStoreParameter implements ProtectionParameter { * * <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 @@ public final class KeyStoreParameter implements ProtectionParameter { * @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 @@ public final class KeyStoreParameter implements ProtectionParameter { 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 c205d9d3df47..8bf228a1c813 100644 --- a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java +++ b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java @@ -23,7 +23,6 @@ import java.security.InvalidKeyException; 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 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { 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 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { 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 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { && (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 @@ public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { 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 65be9e19a56a..a72faeab6268 100644 --- a/libs/hwui/JankTracker.cpp +++ b/libs/hwui/JankTracker.cpp @@ -169,6 +169,10 @@ void JankTracker::switchStorageToAshmem(int ashmemfd) { 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::dumpBuffer(const void* buffer, size_t bufsize, int fd) { } 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 @@ void JankTracker::reset() { 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 478300143553..3887e5e65d60 100644 --- a/libs/hwui/JankTracker.h +++ b/libs/hwui/JankTracker.h @@ -44,10 +44,11 @@ enum JankType { 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/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp index 597d95ce7973..9e7faee69fba 100644 --- a/libs/hwui/Snapshot.cpp +++ b/libs/hwui/Snapshot.cpp @@ -203,8 +203,9 @@ void Snapshot::dump() const { ALOGD("Snapshot %p, flags %x, prev %p, height %d, ignored %d, hasComplexClip %d", this, flags, previous.get(), getViewportHeight(), isIgnored(), !mClipArea->isSimple()); const Rect& clipRect(mClipArea->getClipRect()); - ALOGD(" ClipRect %.1f %.1f %.1f %.1f", - clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); + ALOGD(" ClipRect %.1f %.1f %.1f %.1f, clip simple %d", + clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, mClipArea->isSimple()); + ALOGD(" Transform (at %p):", transform); transform->dump(); } diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index a7f33fac4d21..fd7fca6ee6da 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -540,6 +540,8 @@ final public class MediaCodec { * 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 index 64b4452b9554..dd2368ff6848 100755..100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -527,7 +527,11 @@ public final class CachedBluetoothDevice implements Comparable<CachedBluetoothDe 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 @@ public final class CachedBluetoothDevice implements Comparable<CachedBluetoothDe } /* - * 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 ff6a45a30c5e..64730c26a913 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.Context; 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 @@ public class DndTile extends QSTile<QSTile.BooleanState> { 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 @@ public class DndTile extends QSTile<QSTile.BooleanState> { 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 @@ public class DndTile extends QSTile<QSTile.BooleanState> { 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/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 130d3bca608d..011c02e4d578 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -341,6 +341,12 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } } + /** Dismisses Recents directly to Home without transition animation. */ + void dismissRecentsToHomeWithoutTransitionAnimation() { + finish(); + overridePendingTransition(0, 0); + } + /** Dismisses Recents directly to Home if we currently aren't transitioning. */ boolean dismissRecentsToHome(boolean animated) { SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java index 4cd577dd88f7..b701e0b6f943 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java @@ -209,6 +209,10 @@ public class RecentsResizeTaskDialog extends DialogFragment { } } + // Get rid of the dialog. + dismiss(); + mRecentsActivity.dismissRecentsToHomeWithoutTransitionAnimation(); + // Resize all tasks beginning from the "oldest" one. for (int i = additionalTasks; i >= 0; --i) { if (mTasks[i] != null) { @@ -216,12 +220,8 @@ public class RecentsResizeTaskDialog extends DialogFragment { } } - // Get rid of the dialog. - dismiss(); - mRecentsActivity.dismissRecentsToHomeRaw(false); - - // Show tasks - beginning with the oldest so that the focus ends on the selected one. - // TODO: Remove this once issue b/19893373 is resolved. + // Show tasks as they might not be currently visible - beginning with the oldest so that + // the focus ends on the selected one. for (int i = additionalTasks; i >= 0; --i) { if (mTasks[i] != null) { mRecentsView.launchTask(mTasks[i]); 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 3b8fcccb2dbb..cab152fc7426 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 @@ public class KeyguardAffordanceHelper { @Override public void onAnimationEnd(Animator animation) { mSwipeAnimator = null; - setSwipingInProgress(false); + mSwipingInProgress = false; } }; private Runnable mAnimationEndRunnable = new Runnable() { @@ -117,14 +117,17 @@ public class KeyguardAffordanceHelper { } 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 @@ public class KeyguardAffordanceHelper { 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 @@ public class KeyguardAffordanceHelper { } } - private void setSwipingInProgress(boolean inProgress) { - mSwipingInProgress = inProgress; - if (inProgress) { - mCallback.onSwipingStarted(); - } - } - private boolean rightSwipePossible() { return mRightIcon.getVisibility() == View.VISIBLE; } @@ -323,6 +320,9 @@ public class KeyguardAffordanceHelper { } animator.start(); mSwipeAnimator = animator; + if (snapBack) { + mCallback.onSwipingAborted(); + } } private void startFinishingCircleAnimation(float velocity, Runnable mAnimationEndRunnable) { @@ -451,7 +451,11 @@ public class KeyguardAffordanceHelper { mSwipeAnimator.cancel(); } setTranslation(0.0f, true, animate); - setSwipingInProgress(false); + mMotionCancelled = true; + if (mSwipingInProgress) { + mCallback.onSwipingAborted(); + } + mSwipingInProgress = false; } public interface Callback { @@ -470,7 +474,9 @@ public class KeyguardAffordanceHelper { 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 a247c8e8f074..628ae845fe57 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 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL 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 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL 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 195da465ef67..216730be5d00 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 @@ public class NotificationPanelView extends PanelView implements } @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 34068fddef9d..0dce82ff011e 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 class PreviewInflater { 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 a3d93777d2aa..265e2c6c1e3d 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.Log; 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 class VolumeDialogController { 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 0f967fcae26a..de1c49730aaa 100644 --- a/rs/java/android/renderscript/FieldPacker.java +++ b/rs/java/android/renderscript/FieldPacker.java @@ -47,6 +47,15 @@ public class FieldPacker { // 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 @@ public class FieldPacker { 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; - } - - if (obj instanceof Byte) { - return 1; - } - - 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; + private boolean resize(int newSize) { + if (newSize == mLen) { + return false; } - 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; - } - } - - return 0; + byte[] newData = new byte[newSize]; + System.arraycopy(mData, 0, newData, 0, mPos); + mData = newData; + mLen = newSize; + return true; } - 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 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; + } + } while (retry); } - 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 9d73ac44ab5d..858a9571f46f 100644 --- a/rs/java/android/renderscript/ScriptGroup2.java +++ b/rs/java/android/renderscript/ScriptGroup2.java @@ -126,7 +126,7 @@ public class ScriptGroup2 extends BaseObj { 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 @@ public class ScriptGroup2 extends BaseObj { /** * 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 5c4edd3ae9d8..60e2b6d99b1e 100644 --- a/rs/java/android/renderscript/ScriptIntrinsicBlur.java +++ b/rs/java/android/renderscript/ScriptIntrinsicBlur.java @@ -34,7 +34,7 @@ public final class ScriptIntrinsicBlur extends ScriptIntrinsic { * 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 5cc59e5631ae..96840a2703eb 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.AppWidgetBackupBridge; 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 f10666749981..66cc29ad3795 100644 --- a/services/core/java/com/android/server/AssetAtlasService.java +++ b/services/core/java/com/android/server/AssetAtlasService.java @@ -415,12 +415,20 @@ public class AssetAtlasService extends IAssetAtlas.Stub { 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/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 201c755fbbc4..eb394c3f5d4d 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -2023,6 +2023,8 @@ public class ConnectivityService extends IConnectivityManager.Stub ReapUnvalidatedNetworks.DONT_REAP); } } + NetworkFactoryInfo nfi = mNetworkFactoryInfos.remove(msg.replyTo); + if (DBG && nfi != null) log("unregisterNetworkFactory for " + nfi.name); } // If this method proves to be too slow then we can maintain a separate diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index 456f1fb8841d..a341c95c2971 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -833,6 +833,9 @@ class MountService extends IMountService.Stub 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 4fa0577fad8c..e92443cf9109 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 @@ public final class ActiveServices { 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 cf51004f74c7..18ab3b4b12bb 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.IActivityContainerCallback; 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 @@ public final class ActivityManagerService extends ActivityManagerNative */ 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 @@ public final class ActivityManagerService extends ActivityManagerNative 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 @@ public final class ActivityManagerService extends ActivityManagerNative 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 @@ public final class ActivityManagerService extends ActivityManagerNative 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 @@ public final class ActivityManagerService extends ActivityManagerNative 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 @@ public final class ActivityManagerService extends ActivityManagerNative 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 @@ public final class ActivityManagerService extends ActivityManagerNative } 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 @@ public final class ActivityManagerService extends ActivityManagerNative } } - 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 @@ public final class ActivityManagerService extends ActivityManagerNative @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; + } + final TaskRecord task = r.task; + if (task != null) { + startLockTaskModeLocked(task); } - } finally { - Binder.restoreCallingIdentity(ident); - } - if (task != null) { - startLockTaskMode(task); } } @@ -8535,11 +8550,12 @@ public final class ActivityManagerService extends ActivityManagerNative "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 final class ActivityManagerService extends ActivityManagerNative 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 @@ public final class ActivityManagerService extends ActivityManagerNative //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 @@ public final class ActivityManagerService extends ActivityManagerNative + " 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 @@ public final class ActivityManagerService extends ActivityManagerNative 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 @@ public final class ActivityManagerService extends ActivityManagerNative 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 @@ public final class ActivityManagerService extends ActivityManagerNative } @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 @@ public final class ActivityManagerService extends ActivityManagerNative + " 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 110839162fa6..ce510a9527e5 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 @@ final class ActivityStack { } 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 class ActivityStack { 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 @@ final class ActivityStack { // 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 @@ final class ActivityStack { // 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 @@ final class ActivityStack { } 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { // 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 @@ final class ActivityStack { // 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 @@ final class ActivityStack { } 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { 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 f05e6aaa6a7c..4ccb5ad777d2 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 com.android.server.audio.PlayerRecord.RemotePlaybackState; 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 @@ public class MediaFocusControl implements OnFinished { } 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 43f74fc849f5..4ac2b48e377a 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 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { HdmiDeviceInfo avr = getAvrDeviceInfo(); if (avr != null) { onNewAvrAdded(avr); + } else { + setSystemAudioMode(false, true); } } }); @@ -1615,10 +1617,6 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { 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 63c8d92ee2b2..53ceb2e5d0c8 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 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne } 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 03e9ad5af31c..8a9f3e138275 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 @@ public class AppIdleController extends StateController 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 4c6cb178a8e5..b3d72877de7c 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 class TimeController extends StateController { 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 95d7a52893d4..591dbee93ef7 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.END_DOCUMENT; 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.RemoteException; 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.SparseArray; 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub { 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub { 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 class PackageInstallerService extends IPackageInstaller.Stub { } }; - 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub { 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 class PackageInstallerService extends IPackageInstaller.Stub { } } + 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub { } @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 @@ public class PackageInstallerService extends IPackageInstaller.Stub { 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub { 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub { 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub { } } - 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub { 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub { 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"); + } + + 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 prepareInternalStageDir(File stageDir) throws IOException { + static void prepareStageDir(File stageDir) throws IOException { if (stageDir.exists()) { throw new IOException("Session dir already exists: " + stageDir); } @@ -749,16 +792,34 @@ public class PackageInstallerService extends IPackageInstaller.Stub { } @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 @@ public class PackageInstallerService extends IPackageInstaller.Stub { 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub { @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 @@ public class PackageInstallerService extends IPackageInstaller.Stub { 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub { @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 @@ public class PackageInstallerService extends IPackageInstaller.Stub { } } + /** + * 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 cc1b3ad3d5d1..46db2d8895ec 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_CREAT; 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.ExceptionUtils; 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.IndentingPrintWriter; 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 3c99484314c4..4fea889e3431 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_UPDATE_INCOMPATIB 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.getPreferredInstructionSet; 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.IPackageManager; 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.PackageInfoLite; 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.net.Uri; 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.SystemClock; 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.Os; 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.PrintStreamPrinter; 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.Set; 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { mRequiredVerifierPackage = getRequiredVerifierLPr(); - mInstallerService = new PackageInstallerService(context, this, mAppInstallDir); + mInstallerService = new PackageInstallerService(context, this); mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr(); mIntentFilterVerifier = new IntentVerifierProxy(mContext, @@ -8468,8 +8485,8 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { * @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 @@ public class PackageManagerService extends IPackageManager.Stub { } 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { // 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 @@ public class PackageManagerService extends IPackageManager.Stub { /* 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 @@ public class PackageManagerService extends IPackageManager.Stub { return (installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0; } - protected boolean isExternal() { + protected boolean isExternalAsec() { return (installFlags & PackageManager.INSTALL_EXTERNAL) != 0; } @@ -9883,8 +9904,8 @@ public class PackageManagerService extends IPackageManager.Stub { /** 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 @@ public class PackageManagerService extends IPackageManager.Stub { /** 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 @@ public class PackageManagerService extends IPackageManager.Stub { } try { - final File tempDir = mInstallerService.allocateInternalStageDirLegacy(); + final File tempDir = mInstallerService.allocateStageDirLegacy(volumeUuid); codeFile = tempDir; resourceFile = tempDir; } catch (IOException e) { @@ -9978,8 +9999,9 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { /** 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { abiOverride); final File target; - if (isExternal()) { + if (isExternalAsec()) { target = new UserEnvironment(UserHandle.USER_OWNER).getExternalStorageDirectory(); } else { target = Environment.getDataDirectory(); @@ -10199,7 +10220,7 @@ public class PackageManagerService extends IPackageManager.Stub { } 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { /* * 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { } 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 @@ public class PackageManagerService extends IPackageManager.Stub { } 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { // 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 @@ public class PackageManagerService extends IPackageManager.Stub { } - 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { } } + /** + * 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 @@ public class PackageManagerService extends IPackageManager.Stub { // 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 @@ public class PackageManagerService extends IPackageManager.Stub { } 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 @@ public class PackageManagerService extends IPackageManager.Stub { } // 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 @@ public class PackageManagerService extends IPackageManager.Stub { } } - /** 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 @@ public class PackageManagerService extends IPackageManager.Stub { // 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 daa6d6413dc6..f294b320fd45 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 static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; 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 @@ abstract class PackageSettingBase extends SettingBase { 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 @@ abstract class PackageSettingBase extends SettingBase { origPackage = base.origPackage; installerPackageName = base.installerPackageName; + volumeUuid = base.volumeUuid; keySetData = new PackageKeySetData(base.keySetData); } @@ -183,10 +187,18 @@ abstract class PackageSettingBase extends SettingBase { 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 0d2ef89fa00b..6b7c35c631e8 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.Process; 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 @@ final class Settings { } } - 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 @@ final class Settings { 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 @@ final class Settings { 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 @@ final class Settings { 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 @@ final class Settings { 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 @@ final class Settings { } } installerPackageName = parser.getAttributeValue(null, "installer"); + volumeUuid = parser.getAttributeValue(null, "volumeUuid"); systemStr = parser.getAttributeValue(null, "publicFlags"); if (systemStr != null) { @@ -3081,6 +3106,7 @@ final class Settings { 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 @@ final class Settings { 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 @@ final class Settings { 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/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index b8c26a4ef786..a8c55276d2ae 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -1690,7 +1690,7 @@ public class UserManagerService extends IUserManager.Stub { final Bundle restrictions = new Bundle(); final ArrayList<String> values = new ArrayList<>(); if (!restrictionsFile.getBaseFile().exists()) { - return null; + return restrictions; } FileInputStream fis = null; diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 98ac5ef1b59a..34120a054d9b 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 @@ class Task { } } + 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 b61a6f74b72d..e845f833eab2 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 @@ public class TaskStack { 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 @@ public class TaskStack { 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 d365759ca725..7fab5a601980 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 ac1b0f1cbf23..1a30cbaea43d 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 @@ class WindowStateAnimator { 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 45584cdfd5d8..bb3085e84443 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - final SparseArray<DevicePolicyData> mUserData = new SparseArray<DevicePolicyData>(); + final SparseArray<DevicePolicyData> mUserData = new SparseArray<>(); Handler mHandler = new Handler(); @@ -1596,6 +1593,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // 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 235567c8f716..4498b84aae09 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 @@ package com.android.server.usage; 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 @@ import java.util.List; * 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 @@ class UsageStatsDatabase { 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 @@ class UsageStatsDatabase { } } - checkVersionLocked(); + checkVersionAndBuildLocked(); indexFilesLocked(); // Delete files that are in the future. @@ -194,10 +197,35 @@ class UsageStatsDatabase { } } - 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 @@ class UsageStatsDatabase { 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 @@ class UsageStatsDatabase { } } + 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 f458dbcebbc4..cc0ab8150798 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 @@ public class UsageStatsService extends SystemService implements 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 afe27c75038b..0a9481acf70c 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 @@ package com.android.server.usage; 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 @@ class UserUsageStatsService { private final UnixCalendar mDailyExpiryDate; private final StatsUpdatedListener mListener; private final String mLogPrefix; + private final int mUserId; interface StatsUpdatedListener { void onStatsUpdated(); @@ -73,6 +77,7 @@ class UserUsageStatsService { mCurrentStats = new IntervalStats[UsageStatsManager.INTERVAL_COUNT]; mListener = listener; mLogPrefix = "User[" + Integer.toString(userId) + "] "; + mUserId = userId; } void init(final long currentTimeMillis) { @@ -128,6 +133,35 @@ class UserUsageStatsService { 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 1aa0d0bf3e29..bca757bc6945 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 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne } 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 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne 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 73c73639af06..607df2dce321 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 @@ package com.android.server.voiceinteraction; 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 class VoiceInteractionSessionConnection implements ServiceConnection { final int mCallingUid; final IActivityManager mAm; final IWindowManager mIWindowManager; + final AppOpsManager mAppOps; final IBinder mPermissionOwner; boolean mShown; Bundle mShowArgs; @@ -148,6 +150,7 @@ final class VoiceInteractionSessionConnection implements ServiceConnection { 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 @@ final class VoiceInteractionSessionConnection implements ServiceConnection { 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 @@ final class VoiceInteractionSessionConnection implements ServiceConnection { 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 @@ final class VoiceInteractionSessionConnection implements ServiceConnection { 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 a204376ffef5..c8b684694f04 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -485,14 +485,19 @@ public class MockPackageManager extends PackageManager { 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 000000000000..b6316f0ff10e --- /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 000000000000..eb402008319d --- /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 000000000000..64437bc06a5c --- /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 000000000000..11f7ac7b8598 --- /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 000000000000..4d22234b1ecc --- /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 000000000000..d49f96d0a38c --- /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 000000000000..530fe0063954 --- /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 8c8151d59ca0..6209bd083cc1 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> - <Button android:id="@+id/pick" - android:layout_width="wrap_content" + <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" - android:text="@string/pickVoice" + 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/abort" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="16dp" - android:text="@string/abortVoice" + <Button android:id="@+id/jump" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/jumpOut" /> - <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 942c93196b84..6289929f5604 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 ec727c4ab78b..3c5c20150061 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 @@ public class MainInteractionSession extends VoiceInteractionSession @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 e195c30c1f7b..9d24c59dd877 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 @@ package com.android.test.voiceinteraction; 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 @@ public class TestInteractionActivity extends Activity implements View.OnClickLis Button mAbortButton; Button mCompleteButton; Button mPickButton; + Button mJumpOutButton; Button mCancelButton; @Override @@ -64,6 +66,8 @@ public class TestInteractionActivity extends Activity implements View.OnClickLis 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 @@ public class TestInteractionActivity extends Activity implements View.OnClickLis } }; 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 c747d92a55f6..9713a4cc4433 100755 --- a/tools/layoutlib/rename_font/build_font.py +++ b/tools/layoutlib/rename_font/build_font.py @@ -209,15 +209,18 @@ def ends_in_regular(string): 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 5f7dad9c2b8e..4245cdcd715b 100755 --- a/tools/layoutlib/rename_font/build_font_single.py +++ b/tools/layoutlib/rename_font/build_font_single.py @@ -193,15 +193,18 @@ def ends_in_regular(string): 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__': |