diff options
81 files changed, 1872 insertions, 1083 deletions
diff --git a/api/current.txt b/api/current.txt index 3245c0a61dee..95c43b2d9a31 100644 --- a/api/current.txt +++ b/api/current.txt @@ -101,7 +101,6 @@ package android { field public static final java.lang.String READ_SYNC_SETTINGS = "android.permission.READ_SYNC_SETTINGS"; field public static final java.lang.String READ_SYNC_STATS = "android.permission.READ_SYNC_STATS"; field public static final java.lang.String READ_VOICEMAIL = "com.android.voicemail.permission.READ_VOICEMAIL"; - field public static final java.lang.String READ_WRITE_CONTACT_METADATA = "android.permission.READ_WRITE_CONTACT_METADATA"; field public static final java.lang.String REBOOT = "android.permission.REBOOT"; field public static final java.lang.String RECEIVE_BOOT_COMPLETED = "android.permission.RECEIVE_BOOT_COMPLETED"; field public static final java.lang.String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST"; @@ -4908,6 +4907,7 @@ package android.app { field public static final int DEFAULT_VIBRATE = 2; // 0x2 field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri"; field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText"; + field public static final java.lang.String EXTRA_CHRONOMETER_COUNTS_DOWN = "android.chronometerCountsDown"; field public static final java.lang.String EXTRA_COMPACT_ACTIONS = "android.compactActions"; field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText"; field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon"; @@ -5059,6 +5059,7 @@ package android.app { method public android.app.Notification.Builder setActions(android.app.Notification.Action...); method public android.app.Notification.Builder setAutoCancel(boolean); method public android.app.Notification.Builder setCategory(java.lang.String); + method public android.app.Notification.Builder setChronometerCountsDown(boolean); method public android.app.Notification.Builder setColor(int); method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews); method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence); @@ -20021,6 +20022,7 @@ package android.media { public abstract interface AudioRouting { method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler); method public abstract android.media.AudioDeviceInfo getPreferredDevice(); + method public abstract android.media.AudioDeviceInfo getRoutedDevice(); method public abstract void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener); method public abstract boolean setPreferredDevice(android.media.AudioDeviceInfo); } @@ -34570,10 +34572,11 @@ package android.service.notification { method public int getSuppressedVisualEffects(); method public boolean isAmbient(); method public boolean matchesInterruptionFilter(); - field public static final int IMPORTANCE_DEFAULT = 2; // 0x2 - field public static final int IMPORTANCE_HIGH = 3; // 0x3 - field public static final int IMPORTANCE_LOW = 1; // 0x1 - field public static final int IMPORTANCE_MAX = 4; // 0x4 + field public static final int IMPORTANCE_DEFAULT = 3; // 0x3 + field public static final int IMPORTANCE_HIGH = 4; // 0x4 + field public static final int IMPORTANCE_LOW = 2; // 0x2 + field public static final int IMPORTANCE_MAX = 5; // 0x5 + field public static final int IMPORTANCE_MIN = 1; // 0x1 field public static final int IMPORTANCE_NONE = 0; // 0x0 field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18 } @@ -47215,6 +47218,7 @@ package android.widget { method public void setChar(int, java.lang.String, char); method public void setCharSequence(int, java.lang.String, java.lang.CharSequence); method public void setChronometer(int, long, java.lang.String, boolean); + method public void setChronometerCountsDown(int, boolean); method public void setContentDescription(int, java.lang.CharSequence); method public void setDisplayedChild(int, int); method public void setDouble(int, java.lang.String, double); diff --git a/api/system-current.txt b/api/system-current.txt index 9a221016051d..47c642f2eb24 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -169,7 +169,6 @@ package android { field public static final java.lang.String READ_SYNC_STATS = "android.permission.READ_SYNC_STATS"; field public static final java.lang.String READ_VOICEMAIL = "com.android.voicemail.permission.READ_VOICEMAIL"; field public static final java.lang.String READ_WIFI_CREDENTIAL = "android.permission.READ_WIFI_CREDENTIAL"; - field public static final java.lang.String READ_WRITE_CONTACT_METADATA = "android.permission.READ_WRITE_CONTACT_METADATA"; field public static final java.lang.String REAL_GET_TASKS = "android.permission.REAL_GET_TASKS"; field public static final java.lang.String REBOOT = "android.permission.REBOOT"; field public static final java.lang.String RECEIVE_BOOT_COMPLETED = "android.permission.RECEIVE_BOOT_COMPLETED"; @@ -5040,6 +5039,7 @@ package android.app { field public static final int DEFAULT_VIBRATE = 2; // 0x2 field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri"; field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText"; + field public static final java.lang.String EXTRA_CHRONOMETER_COUNTS_DOWN = "android.chronometerCountsDown"; field public static final java.lang.String EXTRA_COMPACT_ACTIONS = "android.compactActions"; field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText"; field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon"; @@ -5191,6 +5191,7 @@ package android.app { method public android.app.Notification.Builder setActions(android.app.Notification.Action...); method public android.app.Notification.Builder setAutoCancel(boolean); method public android.app.Notification.Builder setCategory(java.lang.String); + method public android.app.Notification.Builder setChronometerCountsDown(boolean); method public android.app.Notification.Builder setColor(int); method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews); method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence); @@ -21529,6 +21530,7 @@ package android.media { public abstract interface AudioRouting { method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler); method public abstract android.media.AudioDeviceInfo getPreferredDevice(); + method public abstract android.media.AudioDeviceInfo getRoutedDevice(); method public abstract void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener); method public abstract boolean setPreferredDevice(android.media.AudioDeviceInfo); } @@ -37113,10 +37115,11 @@ package android.service.notification { method public int getSuppressedVisualEffects(); method public boolean isAmbient(); method public boolean matchesInterruptionFilter(); - field public static final int IMPORTANCE_DEFAULT = 2; // 0x2 - field public static final int IMPORTANCE_HIGH = 3; // 0x3 - field public static final int IMPORTANCE_LOW = 1; // 0x1 - field public static final int IMPORTANCE_MAX = 4; // 0x4 + field public static final int IMPORTANCE_DEFAULT = 3; // 0x3 + field public static final int IMPORTANCE_HIGH = 4; // 0x4 + field public static final int IMPORTANCE_LOW = 2; // 0x2 + field public static final int IMPORTANCE_MAX = 5; // 0x5 + field public static final int IMPORTANCE_MIN = 1; // 0x1 field public static final int IMPORTANCE_NONE = 0; // 0x0 field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18 } @@ -50333,6 +50336,7 @@ package android.widget { method public void setChar(int, java.lang.String, char); method public void setCharSequence(int, java.lang.String, java.lang.CharSequence); method public void setChronometer(int, long, java.lang.String, boolean); + method public void setChronometerCountsDown(int, boolean); method public void setContentDescription(int, java.lang.CharSequence); method public void setDisplayedChild(int, int); method public void setDouble(int, java.lang.String, double); diff --git a/api/test-current.txt b/api/test-current.txt index 0b7914bd211d..7a9557c2744f 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -101,7 +101,6 @@ package android { field public static final java.lang.String READ_SYNC_SETTINGS = "android.permission.READ_SYNC_SETTINGS"; field public static final java.lang.String READ_SYNC_STATS = "android.permission.READ_SYNC_STATS"; field public static final java.lang.String READ_VOICEMAIL = "com.android.voicemail.permission.READ_VOICEMAIL"; - field public static final java.lang.String READ_WRITE_CONTACT_METADATA = "android.permission.READ_WRITE_CONTACT_METADATA"; field public static final java.lang.String REBOOT = "android.permission.REBOOT"; field public static final java.lang.String RECEIVE_BOOT_COMPLETED = "android.permission.RECEIVE_BOOT_COMPLETED"; field public static final java.lang.String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST"; @@ -4908,6 +4907,7 @@ package android.app { field public static final int DEFAULT_VIBRATE = 2; // 0x2 field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri"; field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText"; + field public static final java.lang.String EXTRA_CHRONOMETER_COUNTS_DOWN = "android.chronometerCountsDown"; field public static final java.lang.String EXTRA_COMPACT_ACTIONS = "android.compactActions"; field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText"; field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon"; @@ -5059,6 +5059,7 @@ package android.app { method public android.app.Notification.Builder setActions(android.app.Notification.Action...); method public android.app.Notification.Builder setAutoCancel(boolean); method public android.app.Notification.Builder setCategory(java.lang.String); + method public android.app.Notification.Builder setChronometerCountsDown(boolean); method public android.app.Notification.Builder setColor(int); method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews); method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence); @@ -20030,6 +20031,7 @@ package android.media { public abstract interface AudioRouting { method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler); method public abstract android.media.AudioDeviceInfo getPreferredDevice(); + method public abstract android.media.AudioDeviceInfo getRoutedDevice(); method public abstract void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener); method public abstract boolean setPreferredDevice(android.media.AudioDeviceInfo); } @@ -34585,10 +34587,11 @@ package android.service.notification { method public int getSuppressedVisualEffects(); method public boolean isAmbient(); method public boolean matchesInterruptionFilter(); - field public static final int IMPORTANCE_DEFAULT = 2; // 0x2 - field public static final int IMPORTANCE_HIGH = 3; // 0x3 - field public static final int IMPORTANCE_LOW = 1; // 0x1 - field public static final int IMPORTANCE_MAX = 4; // 0x4 + field public static final int IMPORTANCE_DEFAULT = 3; // 0x3 + field public static final int IMPORTANCE_HIGH = 4; // 0x4 + field public static final int IMPORTANCE_LOW = 2; // 0x2 + field public static final int IMPORTANCE_MAX = 5; // 0x5 + field public static final int IMPORTANCE_MIN = 1; // 0x1 field public static final int IMPORTANCE_NONE = 0; // 0x0 field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18 } @@ -47232,6 +47235,7 @@ package android.widget { method public void setChar(int, java.lang.String, char); method public void setCharSequence(int, java.lang.String, java.lang.CharSequence); method public void setChronometer(int, long, java.lang.String, boolean); + method public void setChronometerCountsDown(int, boolean); method public void setContentDescription(int, java.lang.CharSequence); method public void setDisplayedChild(int, int); method public void setDouble(int, java.lang.String, double); diff --git a/cmds/app_process/Android.mk b/cmds/app_process/Android.mk index 51bbb813df65..3ae9e1204632 100644 --- a/cmds/app_process/Android.mk +++ b/cmds/app_process/Android.mk @@ -20,6 +20,7 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ liblog \ libbinder \ + libnativeloader \ libandroid_runtime \ $(app_process_common_shared_libs) \ @@ -52,6 +53,7 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ liblog \ libbinder \ + libnativeloader \ libandroid_runtime \ $(app_process_common_shared_libs) \ diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp index 2e023825a219..8bcbf51cc857 100644 --- a/cmds/app_process/app_main.cpp +++ b/cmds/app_process/app_main.cpp @@ -21,6 +21,7 @@ #include <cutils/properties.h> #include <cutils/trace.h> #include <android_runtime/AndroidRuntime.h> +#include <nativeloader/native_loader.h> #include <private/android_filesystem_config.h> // for AID_SYSTEM namespace android { @@ -304,6 +305,7 @@ int main(int argc, char* const argv[]) } if (zygote) { + PreloadPublicNativeLibraries(); runtime.start("com.android.internal.os.ZygoteInit", args, zygote); } else if (className) { runtime.start("com.android.internal.os.RuntimeInit", args, zygote); diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index b1927d043cce..8b010f3cdb83 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -716,8 +716,7 @@ public class ActivityManager { try { return ActivityManagerNative.getDefault().getFrontActivityScreenCompatMode(); } catch (RemoteException e) { - // System dead, we will be dead too soon! - return 0; + throw e.rethrowAsRuntimeException(); } } @@ -726,7 +725,7 @@ public class ActivityManager { try { ActivityManagerNative.getDefault().setFrontActivityScreenCompatMode(mode); } catch (RemoteException e) { - // System dead, we will be dead too soon! + throw e.rethrowAsRuntimeException(); } } @@ -735,8 +734,7 @@ public class ActivityManager { try { return ActivityManagerNative.getDefault().getPackageScreenCompatMode(packageName); } catch (RemoteException e) { - // System dead, we will be dead too soon! - return 0; + throw e.rethrowAsRuntimeException(); } } @@ -745,7 +743,7 @@ public class ActivityManager { try { ActivityManagerNative.getDefault().setPackageScreenCompatMode(packageName, mode); } catch (RemoteException e) { - // System dead, we will be dead too soon! + throw e.rethrowAsRuntimeException(); } } @@ -754,8 +752,7 @@ public class ActivityManager { try { return ActivityManagerNative.getDefault().getPackageAskScreenCompat(packageName); } catch (RemoteException e) { - // System dead, we will be dead too soon! - return false; + throw e.rethrowAsRuntimeException(); } } @@ -764,7 +761,7 @@ public class ActivityManager { try { ActivityManagerNative.getDefault().setPackageAskScreenCompat(packageName, ask); } catch (RemoteException e) { - // System dead, we will be dead too soon! + throw e.rethrowAsRuntimeException(); } } @@ -1049,6 +1046,7 @@ public class ActivityManager { return ActivityManagerNative.getDefault().getTaskDescriptionIcon(iconFilename, userId); } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); } } return null; @@ -1429,8 +1427,7 @@ public class ActivityManager { return ActivityManagerNative.getDefault().getRecentTasks(maxNum, flags, UserHandle.myUserId()); } catch (RemoteException e) { - // System dead, we will be dead too soon! - return null; + throw e.rethrowAsRuntimeException(); } } @@ -1455,8 +1452,7 @@ public class ActivityManager { return ActivityManagerNative.getDefault().getRecentTasks(maxNum, flags, userId); } catch (RemoteException e) { - // System dead, we will be dead too soon! - return null; + throw e.rethrowAsRuntimeException(); } } @@ -1591,8 +1587,7 @@ public class ActivityManager { try { appTasks = ActivityManagerNative.getDefault().getAppTasks(mContext.getPackageName()); } catch (RemoteException e) { - // System dead, we will be dead too soon! - return null; + throw e.rethrowAsRuntimeException(); } int numAppTasks = appTasks.size(); for (int i = 0; i < numAppTasks; i++) { @@ -1617,7 +1612,7 @@ public class ActivityManager { try { mAppTaskThumbnailSize = ActivityManagerNative.getDefault().getAppTaskThumbnailSize(); } catch (RemoteException e) { - throw new IllegalStateException("System dead?", e); + throw e.rethrowAsRuntimeException(); } } } @@ -1683,7 +1678,7 @@ public class ActivityManager { return ActivityManagerNative.getDefault().addAppTask(activity.getActivityToken(), intent, description, thumbnail); } catch (RemoteException e) { - throw new IllegalStateException("System dead?", e); + throw e.rethrowAsRuntimeException(); } } @@ -1725,8 +1720,7 @@ public class ActivityManager { try { return ActivityManagerNative.getDefault().getTasks(maxNum, 0); } catch (RemoteException e) { - // System dead, we will be dead too soon! - return null; + throw e.rethrowAsRuntimeException(); } } @@ -1742,8 +1736,7 @@ public class ActivityManager { try { return ActivityManagerNative.getDefault().removeTask(taskId); } catch (RemoteException e) { - // System dead, we will be dead too soon! - return false; + throw e.rethrowAsRuntimeException(); } } @@ -1902,8 +1895,7 @@ public class ActivityManager { try { return ActivityManagerNative.getDefault().getTaskThumbnail(id); } catch (RemoteException e) { - // System dead, we will be dead too soon! - return null; + throw e.rethrowAsRuntimeException(); } } @@ -1912,8 +1904,7 @@ public class ActivityManager { try { return ActivityManagerNative.getDefault().isInHomeStack(taskId); } catch (RemoteException e) { - // System dead, we will be dead too soon! - return false; + throw e.rethrowAsRuntimeException(); } } @@ -1962,7 +1953,7 @@ public class ActivityManager { try { ActivityManagerNative.getDefault().moveTaskToFront(taskId, flags, options); } catch (RemoteException e) { - // System dead, we will be dead too soon! + throw e.rethrowAsRuntimeException(); } } @@ -2148,8 +2139,7 @@ public class ActivityManager { return ActivityManagerNative.getDefault() .getServices(maxNum, 0); } catch (RemoteException e) { - // System dead, we will be dead too soon! - return null; + throw e.rethrowAsRuntimeException(); } } @@ -2164,8 +2154,7 @@ public class ActivityManager { return ActivityManagerNative.getDefault() .getRunningServiceControlPanel(service); } catch (RemoteException e) { - // System dead, we will be dead too soon! - return null; + throw e.rethrowAsRuntimeException(); } } @@ -2269,6 +2258,7 @@ public class ActivityManager { try { ActivityManagerNative.getDefault().getMemoryInfo(outInfo); } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); } } @@ -2387,7 +2377,7 @@ public class ActivityManager { return ActivityManagerNative.getDefault().clearApplicationUserData(packageName, observer, UserHandle.myUserId()); } catch (RemoteException e) { - return false; + throw e.rethrowAsRuntimeException(); } } @@ -2421,8 +2411,7 @@ public class ActivityManager { return ActivityManagerNative.getDefault().getGrantedUriPermissions(packageName, UserHandle.myUserId()); } catch (RemoteException e) { - Log.e(TAG, "Couldn't get granted URI permissions for :" + packageName, e); - return ParceledListSlice.emptyList(); + throw e.rethrowAsRuntimeException(); } } @@ -2440,7 +2429,7 @@ public class ActivityManager { ActivityManagerNative.getDefault().clearGrantedUriPermissions(packageName, UserHandle.myUserId()); } catch (RemoteException e) { - Log.e(TAG, "Couldn't clear granted URI permissions for :" + packageName, e); + throw e.rethrowAsRuntimeException(); } } @@ -2560,7 +2549,7 @@ public class ActivityManager { try { return ActivityManagerNative.getDefault().getProcessesInErrorState(); } catch (RemoteException e) { - return null; + throw e.rethrowAsRuntimeException(); } } @@ -2874,7 +2863,7 @@ public class ActivityManager { try { return ActivityManagerNative.getDefault().getRunningExternalApplications(); } catch (RemoteException e) { - return null; + throw e.rethrowAsRuntimeException(); } } @@ -2891,7 +2880,7 @@ public class ActivityManager { return ActivityManagerNative.getDefault().setProcessMemoryTrimLevel(process, userId, level); } catch (RemoteException e) { - return false; + throw e.rethrowAsRuntimeException(); } } @@ -2909,7 +2898,7 @@ public class ActivityManager { try { return ActivityManagerNative.getDefault().getRunningAppProcesses(); } catch (RemoteException e) { - return null; + throw e.rethrowAsRuntimeException(); } } @@ -2928,7 +2917,7 @@ public class ActivityManager { mContext.getOpPackageName()); return RunningAppProcessInfo.procStateToImportance(procState); } catch (RemoteException e) { - return RunningAppProcessInfo.IMPORTANCE_GONE; + throw e.rethrowAsRuntimeException(); } } @@ -2947,6 +2936,7 @@ public class ActivityManager { try { ActivityManagerNative.getDefault().getMyMemoryState(outState); } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); } } @@ -2965,7 +2955,7 @@ public class ActivityManager { try { return ActivityManagerNative.getDefault().getProcessMemoryInfo(pids); } catch (RemoteException e) { - return null; + throw e.rethrowAsRuntimeException(); } } @@ -2999,6 +2989,7 @@ public class ActivityManager { ActivityManagerNative.getDefault().killBackgroundProcesses(packageName, UserHandle.myUserId()); } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); } } @@ -3015,7 +3006,7 @@ public class ActivityManager { ActivityManagerNative.getDefault().killUid(UserHandle.getAppId(uid), UserHandle.getUserId(uid), reason); } catch (RemoteException e) { - Log.e(TAG, "Couldn't kill uid:" + uid, e); + throw e.rethrowAsRuntimeException(); } } @@ -3042,6 +3033,7 @@ public class ActivityManager { try { ActivityManagerNative.getDefault().forceStopPackage(packageName, userId); } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); } } @@ -3060,8 +3052,8 @@ public class ActivityManager { try { return ActivityManagerNative.getDefault().getDeviceConfigurationInfo(); } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); } - return null; } /** @@ -3150,8 +3142,8 @@ public class ActivityManager { try { return ActivityManagerNative.getDefault().isUserAMonkey(); } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); } - return false; } /** @@ -3226,10 +3218,8 @@ public class ActivityManager { return AppGlobals.getPackageManager() .checkUidPermission(permission, uid); } catch (RemoteException e) { - // Should never happen, but if it does... deny! - Slog.e(TAG, "PackageManager is dead?!?", e); + throw e.rethrowAsRuntimeException(); } - return PackageManager.PERMISSION_DENIED; } /** @hide */ @@ -3238,10 +3228,8 @@ public class ActivityManager { return AppGlobals.getPackageManager() .checkUidPermission(permission, uid); } catch (RemoteException e) { - // Should never happen, but if it does... deny! - Slog.e(TAG, "PackageManager is dead?!?", e); + throw e.rethrowAsRuntimeException(); } - return PackageManager.PERMISSION_DENIED; } /** @@ -3277,7 +3265,7 @@ public class ActivityManager { return ActivityManagerNative.getDefault().handleIncomingUser(callingPid, callingUid, userId, allowAll, requireFull, name, callerPackage); } catch (RemoteException e) { - throw new SecurityException("Failed calling activity manager", e); + throw e.rethrowAsRuntimeException(); } } @@ -3292,7 +3280,7 @@ public class ActivityManager { ui = ActivityManagerNative.getDefault().getCurrentUser(); return ui != null ? ui.id : 0; } catch (RemoteException e) { - return 0; + throw e.rethrowAsRuntimeException(); } } @@ -3304,7 +3292,7 @@ public class ActivityManager { try { return ActivityManagerNative.getDefault().switchUser(userid); } catch (RemoteException e) { - return false; + throw e.rethrowAsRuntimeException(); } } @@ -3328,7 +3316,7 @@ public class ActivityManager { try { return ActivityManagerNative.getDefault().isUserRunning(userId, 0); } catch (RemoteException e) { - return false; + throw e.rethrowAsRuntimeException(); } } @@ -3338,7 +3326,7 @@ public class ActivityManager { return ActivityManagerNative.getDefault().isUserRunning(userId, ActivityManager.FLAG_AND_LOCKED); } catch (RemoteException e) { - return false; + throw e.rethrowAsRuntimeException(); } } @@ -3348,7 +3336,7 @@ public class ActivityManager { return ActivityManagerNative.getDefault().isUserRunning(userId, ActivityManager.FLAG_AND_UNLOCKED); } catch (RemoteException e) { - return false; + throw e.rethrowAsRuntimeException(); } } @@ -3433,6 +3421,7 @@ public class ActivityManager { ActivityManagerNative.getDefault().setDumpHeapDebugLimit(null, 0, pssSize, mContext.getPackageName()); } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); } } @@ -3451,6 +3440,7 @@ public class ActivityManager { try { ActivityManagerNative.getDefault().setDumpHeapDebugLimit(null, 0, 0, null); } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); } } @@ -3461,6 +3451,7 @@ public class ActivityManager { try { ActivityManagerNative.getDefault().startLockTaskMode(taskId); } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); } } @@ -3471,6 +3462,7 @@ public class ActivityManager { try { ActivityManagerNative.getDefault().stopLockTaskMode(); } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); } } @@ -3497,7 +3489,7 @@ public class ActivityManager { try { return ActivityManagerNative.getDefault().getLockTaskModeState(); } catch (RemoteException e) { - return ActivityManager.LOCK_TASK_MODE_NONE; + throw e.rethrowAsRuntimeException(); } } @@ -3520,7 +3512,7 @@ public class ActivityManager { try { mAppTaskImpl.finishAndRemoveTask(); } catch (RemoteException e) { - Slog.e(TAG, "Invalid AppTask", e); + throw e.rethrowAsRuntimeException(); } } @@ -3533,8 +3525,7 @@ public class ActivityManager { try { return mAppTaskImpl.getTaskInfo(); } catch (RemoteException e) { - Slog.e(TAG, "Invalid AppTask", e); - return null; + throw e.rethrowAsRuntimeException(); } } @@ -3548,7 +3539,7 @@ public class ActivityManager { try { mAppTaskImpl.moveToFront(); } catch (RemoteException e) { - Slog.e(TAG, "Invalid AppTask", e); + throw e.rethrowAsRuntimeException(); } } @@ -3590,7 +3581,7 @@ public class ActivityManager { try { mAppTaskImpl.setExcludeFromRecents(exclude); } catch (RemoteException e) { - Slog.e(TAG, "Invalid AppTask", e); + throw e.rethrowAsRuntimeException(); } } } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index bb36a3e587b9..a1f82dea9542 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1540,6 +1540,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case GET_MEMORY_TRIM_LEVEL_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + int level = getMemoryTrimLevel(); + reply.writeNoException(); + reply.writeInt(level); + return true; + } + case ENTER_SAFE_MODE_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); enterSafeMode(); @@ -4874,6 +4882,18 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); reply.recycle(); } + public int getMemoryTrimLevel() throws RemoteException + { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + mRemote.transact(GET_MEMORY_TRIM_LEVEL_TRANSACTION, data, reply, 0); + reply.readException(); + int level = reply.readInt(); + data.recycle(); + reply.recycle(); + return level; + } public void enterSafeMode() throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java index 7d0d1b4f5ee5..b20c09125733 100644 --- a/core/java/android/app/ApplicationLoaders.java +++ b/core/java/android/app/ApplicationLoaders.java @@ -20,16 +20,14 @@ import android.os.Trace; import android.util.ArrayMap; import dalvik.system.PathClassLoader; -class ApplicationLoaders -{ - public static ApplicationLoaders getDefault() - { +class ApplicationLoaders { + public static ApplicationLoaders getDefault() { return gApplicationLoaders; } - public ClassLoader getClassLoader(String zip, boolean isBundled, String librarySearchPath, - String libraryPermittedPath, ClassLoader parent) - { + public ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled, + String librarySearchPath, String libraryPermittedPath, + ClassLoader parent) { /* * This is the parent we use if they pass "null" in. In theory * this should be the "system" class loader; in practice we @@ -55,11 +53,22 @@ class ApplicationLoaders } Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip); + PathClassLoader pathClassloader = - new PathClassLoader(zip, isBundled, librarySearchPath, - libraryPermittedPath, parent); + new PathClassLoader(zip, librarySearchPath, parent); + + String errorMessage = createClassloaderNamespace(pathClassloader, + targetSdkVersion, + librarySearchPath, + libraryPermittedPath, + isBundled); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + if (errorMessage != null) { + throw new UnsatisfiedLinkError("Unable to create namespace for the classloader " + + pathClassloader + ": " + errorMessage); + } + mLoaders.put(zip, pathClassloader); return pathClassloader; } @@ -71,6 +80,12 @@ class ApplicationLoaders } } + private static native String createClassloaderNamespace(ClassLoader classLoader, + int targetSdkVersion, + String librarySearchPath, + String libraryPermittedPath, + boolean isShared); + private final ArrayMap<String, ClassLoader> mLoaders = new ArrayMap<String, ClassLoader>(); private static final ApplicationLoaders gApplicationLoaders diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 91eabcc36d09..6d716cc869eb 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -143,7 +143,7 @@ public class ApplicationPackageManager extends PackageManager { return pi; } } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } throw new NameNotFoundException(packageName); @@ -154,7 +154,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.currentToCanonicalPackageNames(names); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -163,7 +163,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.canonicalToCurrentPackageNames(names); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -227,7 +227,7 @@ public class ApplicationPackageManager extends PackageManager { return gids; } } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } throw new NameNotFoundException(packageName); @@ -252,7 +252,7 @@ public class ApplicationPackageManager extends PackageManager { return uid; } } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } throw new NameNotFoundException(packageName); @@ -267,7 +267,7 @@ public class ApplicationPackageManager extends PackageManager { return pi; } } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } throw new NameNotFoundException(name); @@ -282,7 +282,7 @@ public class ApplicationPackageManager extends PackageManager { return pi; } } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } throw new NameNotFoundException(group); @@ -297,7 +297,7 @@ public class ApplicationPackageManager extends PackageManager { return pgi; } } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } throw new NameNotFoundException(name); @@ -308,7 +308,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.getAllPermissionGroups(flags); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -330,7 +330,7 @@ public class ApplicationPackageManager extends PackageManager { return maybeAdjustApplicationInfo(ai); } } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } throw new NameNotFoundException(packageName); @@ -370,7 +370,7 @@ public class ApplicationPackageManager extends PackageManager { return ai; } } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } throw new NameNotFoundException(className.toString()); @@ -385,7 +385,7 @@ public class ApplicationPackageManager extends PackageManager { return ai; } } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } throw new NameNotFoundException(className.toString()); @@ -400,7 +400,7 @@ public class ApplicationPackageManager extends PackageManager { return si; } } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } throw new NameNotFoundException(className.toString()); @@ -415,7 +415,7 @@ public class ApplicationPackageManager extends PackageManager { return pi; } } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } throw new NameNotFoundException(className.toString()); @@ -426,7 +426,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.getSystemSharedLibraryNames(); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -436,7 +436,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.getServicesSystemSharedLibraryPackageName(); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -445,7 +445,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.getSystemAvailableFeatures(); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -459,7 +459,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.hasSystemFeature(name, version); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -468,7 +468,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.checkPermission(permName, pkgName, mContext.getUserId()); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -477,7 +477,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.isPermissionRevokedByPolicy(permName, pkgName, mContext.getUserId()); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -491,7 +491,7 @@ public class ApplicationPackageManager extends PackageManager { try { mPermissionsControllerPackageName = mPM.getPermissionControllerPackageName(); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } return mPermissionsControllerPackageName; @@ -503,7 +503,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.addPermission(info); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -512,7 +512,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.addPermissionAsync(info); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -521,7 +521,7 @@ public class ApplicationPackageManager extends PackageManager { try { mPM.removePermission(name); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -531,7 +531,7 @@ public class ApplicationPackageManager extends PackageManager { try { mPM.grantRuntimePermission(packageName, permissionName, user.getIdentifier()); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -541,7 +541,7 @@ public class ApplicationPackageManager extends PackageManager { try { mPM.revokeRuntimePermission(packageName, permissionName, user.getIdentifier()); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -550,7 +550,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.getPermissionFlags(permissionName, packageName, user.getIdentifier()); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -561,7 +561,7 @@ public class ApplicationPackageManager extends PackageManager { mPM.updatePermissionFlags(permissionName, packageName, flagMask, flagValues, user.getIdentifier()); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -571,7 +571,7 @@ public class ApplicationPackageManager extends PackageManager { return mPM.shouldShowRequestPermissionRationale(permission, mContext.getPackageName(), mContext.getUserId()); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -580,7 +580,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.checkSignatures(pkg1, pkg2); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -589,7 +589,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.checkUidSignatures(uid1, uid2); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -598,7 +598,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.getPackagesForUid(uid); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -607,7 +607,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.getNameForUid(uid); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -620,7 +620,7 @@ public class ApplicationPackageManager extends PackageManager { return uid; } } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } throw new NameNotFoundException("No shared userid for user:"+sharedUserName); } @@ -638,7 +638,7 @@ public class ApplicationPackageManager extends PackageManager { ParceledListSlice<PackageInfo> slice = mPM.getInstalledPackages(flags, userId); return slice.getList(); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -652,7 +652,7 @@ public class ApplicationPackageManager extends PackageManager { permissions, flags, userId); return slice.getList(); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -664,7 +664,7 @@ public class ApplicationPackageManager extends PackageManager { ParceledListSlice<ApplicationInfo> slice = mPM.getInstalledApplications(flags, userId); return slice.getList(); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -680,7 +680,7 @@ public class ApplicationPackageManager extends PackageManager { } return Collections.emptyList(); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -695,7 +695,7 @@ public class ApplicationPackageManager extends PackageManager { } return null; } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -705,9 +705,8 @@ public class ApplicationPackageManager extends PackageManager { return mPM.isEphemeralApplication( mContext.getPackageName(), mContext.getUserId()); } catch (RemoteException e) { - Log.e(TAG, "System server is dead", e); + throw e.rethrowAsRuntimeException(); } - return false; } @Override @@ -724,11 +723,12 @@ public class ApplicationPackageManager extends PackageManager { mContext.getPackageName(), mContext.getUserId()); if (cookie != null) { return cookie; + } else { + return EmptyArray.BYTE; } } catch (RemoteException e) { - Log.e(TAG, "System server is dead", e); + throw e.rethrowAsRuntimeException(); } - return EmptyArray.BYTE; } @Override @@ -737,9 +737,8 @@ public class ApplicationPackageManager extends PackageManager { return mPM.setEphemeralApplicationCookie( mContext.getPackageName(), cookie, mContext.getUserId()); } catch (RemoteException e) { - Log.e(TAG, "System server is dead", e); + throw e.rethrowAsRuntimeException(); } - return false; } @Override @@ -756,7 +755,7 @@ public class ApplicationPackageManager extends PackageManager { flags, userId); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -777,7 +776,7 @@ public class ApplicationPackageManager extends PackageManager { flags, userId); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -809,7 +808,7 @@ public class ApplicationPackageManager extends PackageManager { specificTypes, intent, intent.resolveTypeIfNeeded(resolver), flags, mContext.getUserId()); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -825,7 +824,7 @@ public class ApplicationPackageManager extends PackageManager { flags, userId); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -843,7 +842,7 @@ public class ApplicationPackageManager extends PackageManager { flags, mContext.getUserId()); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -856,7 +855,7 @@ public class ApplicationPackageManager extends PackageManager { flags, userId); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -872,7 +871,7 @@ public class ApplicationPackageManager extends PackageManager { return mPM.queryIntentContentProviders(intent, intent.resolveTypeIfNeeded(mContext.getContentResolver()), flags, userId); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -892,7 +891,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.resolveContentProvider(name, flags, userId); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -904,7 +903,7 @@ public class ApplicationPackageManager extends PackageManager { = mPM.queryContentProviders(processName, uid, flags); return slice != null ? slice.getList() : null; } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -919,7 +918,7 @@ public class ApplicationPackageManager extends PackageManager { return ii; } } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } throw new NameNotFoundException(className.toString()); @@ -931,7 +930,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.queryInstrumentation(targetPackage, flags); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -1198,7 +1197,7 @@ public class ApplicationPackageManager extends PackageManager { return getResourcesForApplication(ai); } } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } throw new NameNotFoundException("Package " + appPackageName + " doesn't exist"); } @@ -1213,7 +1212,7 @@ public class ApplicationPackageManager extends PackageManager { } return mCachedSafeMode != 0; } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } @@ -1229,7 +1228,7 @@ public class ApplicationPackageManager extends PackageManager { mPM.addOnPermissionsChangeListener(delegate); mPermissionListeners.put(listener, delegate); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } } @@ -1243,7 +1242,7 @@ public class ApplicationPackageManager extends PackageManager { mPM.removeOnPermissionsChangeListener(delegate); mPermissionListeners.remove(listener); } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } } } @@ -1544,7 +1543,8 @@ public class ApplicationPackageManager extends PackageManager { try { mPM.installPackageAsUser(originPath, observer.getBinder(), flags, installerPackageName, verificationParams, null, userId); - } catch (RemoteException ignored) { + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); } } @@ -1563,8 +1563,7 @@ public class ApplicationPackageManager extends PackageManager { } return res; } catch (RemoteException e) { - // Should never happen! - throw new NameNotFoundException("Package " + packageName + " doesn't exist"); + throw e.rethrowAsRuntimeException(); } } @@ -1573,7 +1572,7 @@ public class ApplicationPackageManager extends PackageManager { try { mPM.verifyPendingInstall(id, response); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } } @@ -1583,7 +1582,7 @@ public class ApplicationPackageManager extends PackageManager { try { mPM.extendVerificationTimeout(id, verificationCodeAtTimeout, millisecondsToDelay); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } } @@ -1592,7 +1591,7 @@ public class ApplicationPackageManager extends PackageManager { try { mPM.verifyIntentFilter(id, verificationCode, outFailedDomains); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } } @@ -1601,8 +1600,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.getIntentVerificationStatus(packageName, userId); } catch (RemoteException e) { - // Should never happen! - return PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; + throw e.rethrowAsRuntimeException(); } } @@ -1611,8 +1609,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.updateIntentVerificationStatus(packageName, status, userId); } catch (RemoteException e) { - // Should never happen! - return false; + throw e.rethrowAsRuntimeException(); } } @@ -1621,8 +1618,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.getIntentFilterVerifications(packageName); } catch (RemoteException e) { - // Should never happen! - return null; + throw e.rethrowAsRuntimeException(); } } @@ -1631,8 +1627,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.getAllIntentFilters(packageName); } catch (RemoteException e) { - // Should never happen! - return null; + throw e.rethrowAsRuntimeException(); } } @@ -1641,8 +1636,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.getDefaultBrowserPackageName(userId); } catch (RemoteException e) { - // Should never happen! - return null; + throw e.rethrowAsRuntimeException(); } } @@ -1651,8 +1645,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.setDefaultBrowserPackageName(packageName, userId); } catch (RemoteException e) { - // Should never happen! - return false; + throw e.rethrowAsRuntimeException(); } } @@ -1662,7 +1655,7 @@ public class ApplicationPackageManager extends PackageManager { try { mPM.setInstallerPackageName(targetPackage, installerPackageName); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } } @@ -1671,9 +1664,8 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.getInstallerPackageName(packageName); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } - return null; } @Override @@ -1796,7 +1788,7 @@ public class ApplicationPackageManager extends PackageManager { return false; } } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); + throw e.rethrowAsRuntimeException(); } // Otherwise we can move to any private volume @@ -1874,7 +1866,7 @@ public class ApplicationPackageManager extends PackageManager { try { mPM.deletePackageAsUser(packageName, observer, userId, flags); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } } @@ -1884,7 +1876,7 @@ public class ApplicationPackageManager extends PackageManager { try { mPM.clearApplicationUserData(packageName, observer, mContext.getUserId()); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } } @Override @@ -1893,7 +1885,7 @@ public class ApplicationPackageManager extends PackageManager { try { mPM.deleteApplicationCacheFiles(packageName, observer); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } } @@ -1903,7 +1895,7 @@ public class ApplicationPackageManager extends PackageManager { try { mPM.freeStorageAndNotify(volumeUuid, idealStorageSize, observer); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } } @@ -1912,7 +1904,7 @@ public class ApplicationPackageManager extends PackageManager { try { mPM.freeStorage(volumeUuid, freeStorageSize, pi); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } } @@ -1922,9 +1914,8 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.setPackagesSuspendedAsUser(packageNames, suspended, userId); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } - return packageNames; } @Override @@ -1932,9 +1923,8 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.isPackageSuspendedForUser(packageName, userId); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } - return false; } @Override @@ -1943,7 +1933,7 @@ public class ApplicationPackageManager extends PackageManager { try { mPM.getPackageSizeInfo(packageName, userHandle, observer); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } } @Override @@ -1951,7 +1941,7 @@ public class ApplicationPackageManager extends PackageManager { try { mPM.addPackageToPreferred(packageName); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } } @@ -1960,7 +1950,7 @@ public class ApplicationPackageManager extends PackageManager { try { mPM.removePackageFromPreferred(packageName); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } } @@ -1969,9 +1959,8 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.getPreferredPackages(flags); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } - return new ArrayList<PackageInfo>(); } @Override @@ -1980,7 +1969,7 @@ public class ApplicationPackageManager extends PackageManager { try { mPM.addPreferredActivity(filter, match, set, activity, mContext.getUserId()); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } } @@ -1990,7 +1979,7 @@ public class ApplicationPackageManager extends PackageManager { try { mPM.addPreferredActivity(filter, match, set, activity, userId); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } } @@ -2000,7 +1989,7 @@ public class ApplicationPackageManager extends PackageManager { try { mPM.replacePreferredActivity(filter, match, set, activity, mContext.getUserId()); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } } @@ -2011,7 +2000,7 @@ public class ApplicationPackageManager extends PackageManager { try { mPM.replacePreferredActivity(filter, match, set, activity, userId); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } } @@ -2020,7 +2009,7 @@ public class ApplicationPackageManager extends PackageManager { try { mPM.clearPackagePreferredActivities(packageName); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } } @@ -2030,9 +2019,8 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.getPreferredActivities(outFilters, outActivities, packageName); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } - return 0; } @Override @@ -2040,9 +2028,8 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.getHomeActivities(outActivities); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } - return null; } @Override @@ -2051,7 +2038,7 @@ public class ApplicationPackageManager extends PackageManager { try { mPM.setComponentEnabledSetting(componentName, newState, flags, mContext.getUserId()); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } } @@ -2060,9 +2047,8 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.getComponentEnabledSetting(componentName, mContext.getUserId()); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } - return PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; } @Override @@ -2072,7 +2058,7 @@ public class ApplicationPackageManager extends PackageManager { mPM.setApplicationEnabledSetting(packageName, newState, flags, mContext.getUserId(), mContext.getOpPackageName()); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } } @@ -2081,9 +2067,8 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.getApplicationEnabledSetting(packageName, mContext.getUserId()); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } - return PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; } @Override @@ -2092,20 +2077,18 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.setApplicationHiddenSettingAsUser(packageName, hidden, user.getIdentifier()); - } catch (RemoteException re) { - // Should never happen! + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); } - return false; } @Override public boolean getApplicationHiddenSettingAsUser(String packageName, UserHandle user) { try { return mPM.getApplicationHiddenSettingAsUser(packageName, user.getIdentifier()); - } catch (RemoteException re) { - // Should never happen! + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); } - return false; } /** @hide */ @@ -2113,26 +2096,22 @@ public class ApplicationPackageManager extends PackageManager { public KeySet getKeySetByAlias(String packageName, String alias) { Preconditions.checkNotNull(packageName); Preconditions.checkNotNull(alias); - KeySet ks; try { - ks = mPM.getKeySetByAlias(packageName, alias); + return mPM.getKeySetByAlias(packageName, alias); } catch (RemoteException e) { - return null; + throw e.rethrowAsRuntimeException(); } - return ks; } /** @hide */ @Override public KeySet getSigningKeySet(String packageName) { Preconditions.checkNotNull(packageName); - KeySet ks; try { - ks = mPM.getSigningKeySet(packageName); + return mPM.getSigningKeySet(packageName); } catch (RemoteException e) { - return null; + throw e.rethrowAsRuntimeException(); } - return ks; } /** @hide */ @@ -2143,7 +2122,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.isPackageSignedByKeySet(packageName, ks); } catch (RemoteException e) { - return false; + throw e.rethrowAsRuntimeException(); } } @@ -2155,7 +2134,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.isPackageSignedByKeySetExactly(packageName, ks); } catch (RemoteException e) { - return false; + throw e.rethrowAsRuntimeException(); } } @@ -2167,9 +2146,8 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.getVerifierDeviceIdentity(); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } - return null; } /** @@ -2180,7 +2158,7 @@ public class ApplicationPackageManager extends PackageManager { try { return mPM.isUpgrade(); } catch (RemoteException e) { - return false; + throw e.rethrowAsRuntimeException(); } } @@ -2218,7 +2196,7 @@ public class ApplicationPackageManager extends PackageManager { mPM.addCrossProfileIntentFilter(filter, mContext.getOpPackageName(), sourceUserId, targetUserId, flags); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } } @@ -2230,7 +2208,7 @@ public class ApplicationPackageManager extends PackageManager { try { mPM.clearCrossProfileIntentFilters(sourceUserId, mContext.getOpPackageName()); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index e4d6835a5d3e..2cb615103024 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -308,6 +308,7 @@ public interface IActivityManager extends IInterface { public void setActivityController(IActivityController watcher) throws RemoteException; public void setLenientBackgroundCheck(boolean enabled) throws RemoteException; + public int getMemoryTrimLevel() throws RemoteException; public void enterSafeMode() throws RemoteException; @@ -980,4 +981,5 @@ public interface IActivityManager extends IInterface { int NOTIFY_PINNED_STACK_ANIMATION_ENDED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 366; int REMOVE_STACK = IBinder.FIRST_CALL_TRANSACTION + 367; int SET_LENIENT_BACKGROUND_CHECK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+368; + int GET_MEMORY_TRIM_LEVEL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+369; } diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 837ceb6cc252..838b8cbc0c33 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -413,8 +413,9 @@ public final class LoadedApk { // as this is early and necessary. StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); - mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, isBundledApp, - librarySearchPath, libraryPermittedPath, mBaseClassLoader); + mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, + mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath, + libraryPermittedPath, mBaseClassLoader); StrictMode.setThreadPolicy(oldPolicy); return mClassLoader; diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java index 3c7f48ba580c..b6e0467154c0 100644 --- a/core/java/android/app/NativeActivity.java +++ b/core/java/android/app/NativeActivity.java @@ -95,8 +95,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, private native long loadNativeCode(String path, String funcname, MessageQueue queue, String internalDataPath, String obbPath, String externalDataPath, int sdkVersion, - AssetManager assetMgr, byte[] savedState, ClassLoader classLoader, String libraryPath, - String isolationPath); + AssetManager assetMgr, byte[] savedState, ClassLoader classLoader, String libraryPath); private native String getDlError(); private native void unloadNativeCode(long handle); private native void onStartNative(long handle); @@ -177,8 +176,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, getAbsolutePath(getFilesDir()), getAbsolutePath(getObbDir()), getAbsolutePath(getExternalFilesDir(null)), Build.VERSION.SDK_INT, getAssets(), nativeSavedState, - classLoader, classLoader.getLdLibraryPath(), - classLoader.getLibraryPermittedPath()); + classLoader, classLoader.getLdLibraryPath()); if (mNativeHandle == 0) { throw new UnsatisfiedLinkError( diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 402c112fdb63..d98c7179888f 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -823,6 +823,12 @@ public class Notification implements Parcelable public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer"; /** + * {@link #extras} key: whether the chronometer set on the notification should count down + * instead of counting up. Is only relevant if key {@link #EXTRA_SHOW_CHRONOMETER} is present. + */ + public static final String EXTRA_CHRONOMETER_COUNTS_DOWN = "android.chronometerCountsDown"; + + /** * {@link #extras} key: whether {@link #when} should be shown, * as supplied to {@link Builder#setShowWhen(boolean)}. */ @@ -2158,8 +2164,12 @@ public class Notification implements Parcelable * * Useful when showing an elapsed time (like an ongoing phone call). * + * The counter can also be set to count down to <code>when</code> when using + * {@link #setChronometerCountsDown(boolean)}. + * * @see android.widget.Chronometer * @see Notification#when + * @see #setChronometerCountsDown(boolean) */ public Builder setUsesChronometer(boolean b) { mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b); @@ -2167,6 +2177,19 @@ public class Notification implements Parcelable } /** + * Sets the Chronometer to count down instead of counting up. + * + * <p>This is only relevant if {@link #setUsesChronometer(boolean)} has been set to true. + * If it isn't set the chronometer will count up. + * + * @see #setUsesChronometer(boolean) + */ + public Builder setChronometerCountsDown(boolean countsDown) { + mN.extras.putBoolean(EXTRA_CHRONOMETER_COUNTS_DOWN, countsDown); + return this; + } + + /** * Set the small icon resource, which will be used to represent the notification in the * status bar. * @@ -3097,6 +3120,8 @@ public class Notification implements Parcelable contentView.setLong(R.id.chronometer, "setBase", mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis())); contentView.setBoolean(R.id.chronometer, "setStarted", true); + boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNTS_DOWN); + contentView.setChronometerCountsDown(R.id.chronometer, countsDown); } else { contentView.setViewVisibility(R.id.time, View.VISIBLE); contentView.setLong(R.id.time, "setTime", mN.when); @@ -3328,6 +3353,8 @@ public class Notification implements Parcelable savedBundle.getBoolean(EXTRA_SHOW_WHEN)); publicExtras.putBoolean(EXTRA_SHOW_CHRONOMETER, savedBundle.getBoolean(EXTRA_SHOW_CHRONOMETER)); + publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNTS_DOWN, + savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNTS_DOWN)); publicExtras.putCharSequence(EXTRA_TITLE, mContext.getString(R.string.notification_hidden_text)); mN.extras = publicExtras; diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java index 4cbaf6cbf087..039c9d7c3c31 100644 --- a/core/java/android/app/job/JobInfo.java +++ b/core/java/android/app/job/JobInfo.java @@ -102,12 +102,19 @@ public class JobInfo implements Parcelable { public static final int PRIORITY_SYNC_INITIALIZATION = 20; /** - * Value of {@link #getPriority} for the current foreground app (overrides the supplied + * Value of {@link #getPriority} for a foreground app (overrides the supplied * JobInfo priority if it is smaller). * @hide */ public static final int PRIORITY_FOREGROUND_APP = 30; + /** + * Value of {@link #getPriority} for the current top app (overrides the supplied + * JobInfo priority if it is smaller). + * @hide + */ + public static final int PRIORITY_TOP_APP = 40; + private final int jobId; private final PersistableBundle extras; private final ComponentName service; diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index e58744bfd905..fb16150b9abf 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -98,13 +98,15 @@ public final class Debug /** * Default trace file path and file */ - private static final String DEFAULT_TRACE_PATH_PREFIX = - Environment.getLegacyExternalStorageDirectory().getPath() + "/"; private static final String DEFAULT_TRACE_BODY = "dmtrace"; private static final String DEFAULT_TRACE_EXTENSION = ".trace"; - private static final String DEFAULT_TRACE_FILE_PATH = - DEFAULT_TRACE_PATH_PREFIX + DEFAULT_TRACE_BODY - + DEFAULT_TRACE_EXTENSION; + private static class NoPreloadHolder { + private static final String DEFAULT_TRACE_PATH_PREFIX = + Environment.getLegacyExternalStorageDirectory().getPath() + "/"; + private static final String DEFAULT_TRACE_FILE_PATH = + DEFAULT_TRACE_PATH_PREFIX + DEFAULT_TRACE_BODY + + DEFAULT_TRACE_EXTENSION; + } /** @@ -942,7 +944,7 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo * tracing. */ public static void startMethodTracing() { - VMDebug.startMethodTracing(DEFAULT_TRACE_FILE_PATH, 0, 0, false, 0); + VMDebug.startMethodTracing(fixTraceName(null), 0, 0, false, 0); } /** @@ -1032,9 +1034,9 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo */ private static String fixTraceName(String traceName) { if (traceName == null) - traceName = DEFAULT_TRACE_FILE_PATH; + traceName = NoPreloadHolder.DEFAULT_TRACE_FILE_PATH; if (traceName.charAt(0) != '/') - traceName = DEFAULT_TRACE_PATH_PREFIX + traceName; + traceName = NoPreloadHolder.DEFAULT_TRACE_PATH_PREFIX + traceName; if (!traceName.endsWith(DEFAULT_TRACE_EXTENSION)) traceName = traceName + DEFAULT_TRACE_EXTENSION; diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index da7f85fc0e1d..0edc43c755b4 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -36,7 +36,6 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.storage.StorageManager; import android.provider.Settings; -import android.util.Log; import android.view.WindowManager.LayoutParams; import com.android.internal.R; @@ -700,8 +699,7 @@ public class UserManager { try { return mService.getUserInfo(getUserHandle()).name; } catch (RemoteException re) { - Log.w(TAG, "Could not get user name", re); - return ""; + throw re.rethrowAsRuntimeException(); } } @@ -771,8 +769,7 @@ public class UserManager { try { return mService.isRestricted(); } catch (RemoteException re) { - Log.w(TAG, "Could not check if user is limited ", re); - return false; + throw re.rethrowAsRuntimeException(); } } @@ -784,8 +781,7 @@ public class UserManager { try { return mService.canHaveRestrictedProfile(userId); } catch (RemoteException re) { - Log.w(TAG, "Could not check if user can have restricted profile", re); - return false; + throw re.rethrowAsRuntimeException(); } } @@ -847,8 +843,8 @@ public class UserManager { public boolean isUserRunning(int userId) { try { return ActivityManagerNative.getDefault().isUserRunning(userId, 0); - } catch (RemoteException e) { - return false; + } catch (RemoteException re) { + throw re.rethrowAsRuntimeException(); } } @@ -864,8 +860,8 @@ public class UserManager { // TODO: reconcile stopped vs stopping? return ActivityManagerNative.getDefault().isUserRunning( user.getIdentifier(), ActivityManager.FLAG_OR_STOPPED); - } catch (RemoteException e) { - return false; + } catch (RemoteException re) { + throw re.rethrowAsRuntimeException(); } } @@ -893,8 +889,8 @@ public class UserManager { try { return ActivityManagerNative.getDefault().isUserRunning( user.getIdentifier(), ActivityManager.FLAG_AND_LOCKED); - } catch (RemoteException e) { - return false; + } catch (RemoteException re) { + throw re.rethrowAsRuntimeException(); } } @@ -922,8 +918,8 @@ public class UserManager { try { return ActivityManagerNative.getDefault().isUserRunning( user.getIdentifier(), ActivityManager.FLAG_AND_UNLOCKED); - } catch (RemoteException e) { - return false; + } catch (RemoteException re) { + throw re.rethrowAsRuntimeException(); } } @@ -968,8 +964,7 @@ public class UserManager { try { return mService.getUserInfo(userHandle); } catch (RemoteException re) { - Log.w(TAG, "Could not get user info", re); - return null; + throw re.rethrowAsRuntimeException(); } } @@ -990,8 +985,7 @@ public class UserManager { try { return mService.getUserRestrictions(userHandle.getIdentifier()); } catch (RemoteException re) { - Log.w(TAG, "Could not get user restrictions", re); - return Bundle.EMPTY; + throw re.rethrowAsRuntimeException(); } } @@ -1007,9 +1001,7 @@ public class UserManager { try { return mService.hasBaseUserRestriction(restrictionKey, userHandle.getIdentifier()); } catch (RemoteException re) { - Log.w(TAG, "Could not get base user restrictions for user " + - userHandle.getIdentifier(), re); - return false; + throw re.rethrowAsRuntimeException(); } } @@ -1065,7 +1057,7 @@ public class UserManager { try { mService.setUserRestriction(key, value, userHandle.getIdentifier()); } catch (RemoteException re) { - Log.w(TAG, "Could not set user restriction", re); + throw re.rethrowAsRuntimeException(); } } @@ -1092,8 +1084,7 @@ public class UserManager { return mService.hasUserRestriction(restrictionKey, userHandle.getIdentifier()); } catch (RemoteException re) { - Log.w(TAG, "Could not check user restrictions", re); - return false; + throw re.rethrowAsRuntimeException(); } } @@ -1147,7 +1138,7 @@ public class UserManager { mService.setUserRestriction(DISALLOW_OUTGOING_CALLS, true, user.id); } } catch (RemoteException re) { - Log.w(TAG, "Could not create a user", re); + throw re.rethrowAsRuntimeException(); } return user; } @@ -1167,7 +1158,7 @@ public class UserManager { Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id); } } catch (RemoteException re) { - Log.w(TAG, "Could not create a user", re); + throw re.rethrowAsRuntimeException(); } return guest; } @@ -1188,8 +1179,7 @@ public class UserManager { try { return mService.createProfileForUser(name, flags, userHandle); } catch (RemoteException re) { - Log.w(TAG, "Could not create a user", re); - return null; + throw re.rethrowAsRuntimeException(); } } @@ -1211,10 +1201,9 @@ public class UserManager { UserHandle.of(user.id)); } return user; - } catch (RemoteException e) { - Log.w(TAG, "Could not create a restricted profile", e); + } catch (RemoteException re) { + throw re.rethrowAsRuntimeException(); } - return null; } /** @@ -1282,8 +1271,7 @@ public class UserManager { try { return mService.getSeedAccountName(); } catch (RemoteException re) { - Log.w(TAG, "Could not get the seed account name", re); - return null; + throw re.rethrowAsRuntimeException(); } } @@ -1297,8 +1285,7 @@ public class UserManager { try { return mService.getSeedAccountType(); } catch (RemoteException re) { - Log.w(TAG, "Could not get the seed account type", re); - return null; + throw re.rethrowAsRuntimeException(); } } @@ -1314,8 +1301,7 @@ public class UserManager { try { return mService.getSeedAccountOptions(); } catch (RemoteException re) { - Log.w(TAG, "Could not get the seed account options", re); - return null; + throw re.rethrowAsRuntimeException(); } } @@ -1336,7 +1322,7 @@ public class UserManager { mService.setSeedAccountData(userId, accountName, accountType, accountOptions, /* persist= */ true); } catch (RemoteException re) { - Log.w(TAG, "Could not set the seed account data", re); + throw re.rethrowAsRuntimeException(); } } @@ -1349,7 +1335,7 @@ public class UserManager { try { mService.clearSeedAccountData(); } catch (RemoteException re) { - Log.w(TAG, "Could not clear the seed account data", re); + throw re.rethrowAsRuntimeException(); } } @@ -1364,8 +1350,7 @@ public class UserManager { try { return mService.markGuestForDeletion(userHandle); } catch (RemoteException re) { - Log.w(TAG, "Could not mark guest for deletion", re); - return false; + throw re.rethrowAsRuntimeException(); } } @@ -1384,8 +1369,8 @@ public class UserManager { public void setUserEnabled(@UserIdInt int userHandle) { try { mService.setUserEnabled(userHandle); - } catch (RemoteException e) { - Log.w(TAG, "Could not enable the profile", e); + } catch (RemoteException re) { + throw re.rethrowAsRuntimeException(); } } @@ -1407,8 +1392,7 @@ public class UserManager { try { return mService.getUsers(false); } catch (RemoteException re) { - Log.w(TAG, "Could not get user list", re); - return null; + throw re.rethrowAsRuntimeException(); } } @@ -1430,8 +1414,7 @@ public class UserManager { } return result; } catch (RemoteException re) { - Log.w(TAG, "Could not get users list", re); - return null; + throw re.rethrowAsRuntimeException(); } } @@ -1447,8 +1430,7 @@ public class UserManager { try { return mService.getUserAccount(userHandle); } catch (RemoteException re) { - Log.w(TAG, "Could not get user account", re); - return null; + throw re.rethrowAsRuntimeException(); } } @@ -1464,7 +1446,7 @@ public class UserManager { try { mService.setUserAccount(userHandle, accountName); } catch (RemoteException re) { - Log.w(TAG, "Could not set user account", re); + throw re.rethrowAsRuntimeException(); } } @@ -1479,8 +1461,7 @@ public class UserManager { try { return mService.getPrimaryUser(); } catch (RemoteException re) { - Log.w(TAG, "Could not get Primary user", re); - return null; + throw re.rethrowAsRuntimeException(); } } @@ -1517,8 +1498,7 @@ public class UserManager { try { return mService.canAddMoreManagedProfiles(userId, allowedToRemoveOne); } catch (RemoteException re) { - Log.w(TAG, "Could not check if we can add more managed profiles", re); - return false; + throw re.rethrowAsRuntimeException(); } } @@ -1537,8 +1517,7 @@ public class UserManager { try { return mService.getProfiles(userHandle, false /* enabledOnly */); } catch (RemoteException re) { - Log.w(TAG, "Could not get user list", re); - return null; + throw re.rethrowAsRuntimeException(); } } @@ -1553,8 +1532,7 @@ public class UserManager { try { return mService.isSameProfileGroup(userId, otherUserId); } catch (RemoteException re) { - Log.w(TAG, "Could not get user list", re); - return false; + throw re.rethrowAsRuntimeException(); } } @@ -1572,8 +1550,7 @@ public class UserManager { try { return mService.getProfiles(userHandle, true /* enabledOnly */); } catch (RemoteException re) { - Log.w(TAG, "Could not get user list", re); - return null; + throw re.rethrowAsRuntimeException(); } } @@ -1589,8 +1566,7 @@ public class UserManager { try { users = mService.getProfiles(UserHandle.myUserId(), true /* enabledOnly */); } catch (RemoteException re) { - Log.w(TAG, "Could not get user list", re); - return null; + throw re.rethrowAsRuntimeException(); } for (UserInfo info : users) { UserHandle userHandle = new UserHandle(info.id); @@ -1610,8 +1586,7 @@ public class UserManager { try { return mService.getCredentialOwnerProfile(userHandle); } catch (RemoteException re) { - Log.w(TAG, "Could not get credential owner", re); - return -1; + throw re.rethrowAsRuntimeException(); } } @@ -1625,8 +1600,7 @@ public class UserManager { try { return mService.getProfileParent(userHandle); } catch (RemoteException re) { - Log.w(TAG, "Could not get profile parent", re); - return null; + throw re.rethrowAsRuntimeException(); } } @@ -1640,8 +1614,8 @@ public class UserManager { public void setQuietModeEnabled(@UserIdInt int userHandle, boolean enableQuietMode) { try { mService.setQuietModeEnabled(userHandle, enableQuietMode); - } catch (RemoteException e) { - Log.w(TAG, "Could not change the profile's quiet mode", e); + } catch (RemoteException re) { + throw re.rethrowAsRuntimeException(); } } @@ -1654,10 +1628,9 @@ public class UserManager { public boolean isQuietModeEnabled(UserHandle userHandle) { try { return mService.isQuietModeEnabled(userHandle.getIdentifier()); - } catch (RemoteException e) { - Log.w(TAG, "Could not query the profile's quiet mode", e); + } catch (RemoteException re) { + throw re.rethrowAsRuntimeException(); } - return false; } /** @@ -1742,8 +1715,7 @@ public class UserManager { try { return mService.getUsers(excludeDying); } catch (RemoteException re) { - Log.w(TAG, "Could not get user list", re); - return null; + throw re.rethrowAsRuntimeException(); } } @@ -1757,8 +1729,7 @@ public class UserManager { try { return mService.removeUser(userHandle); } catch (RemoteException re) { - Log.w(TAG, "Could not remove user ", re); - return false; + throw re.rethrowAsRuntimeException(); } } @@ -1774,7 +1745,7 @@ public class UserManager { try { mService.setUserName(userHandle, name); } catch (RemoteException re) { - Log.w(TAG, "Could not set the user name ", re); + throw re.rethrowAsRuntimeException(); } } @@ -1788,7 +1759,7 @@ public class UserManager { try { mService.setUserIcon(userHandle, icon); } catch (RemoteException re) { - Log.w(TAG, "Could not set the user icon ", re); + throw re.rethrowAsRuntimeException(); } } @@ -1813,7 +1784,7 @@ public class UserManager { } } } catch (RemoteException re) { - Log.w(TAG, "Could not get the user icon ", re); + throw re.rethrowAsRuntimeException(); } return null; } @@ -1869,9 +1840,8 @@ public class UserManager { try { return mService.getUserSerialNumber(userHandle); } catch (RemoteException re) { - Log.w(TAG, "Could not get serial number for user " + userHandle); + throw re.rethrowAsRuntimeException(); } - return -1; } /** @@ -1887,9 +1857,8 @@ public class UserManager { try { return mService.getUserHandle(userSerialNumber); } catch (RemoteException re) { - Log.w(TAG, "Could not get userHandle for user " + userSerialNumber); + throw re.rethrowAsRuntimeException(); } - return -1; } /** @@ -1915,9 +1884,8 @@ public class UserManager { try { return mService.getApplicationRestrictions(packageName); } catch (RemoteException re) { - Log.w(TAG, "Could not get application restrictions for package " + packageName); + throw re.rethrowAsRuntimeException(); } - return null; } /** @@ -1927,9 +1895,8 @@ public class UserManager { try { return mService.getApplicationRestrictionsForUser(packageName, user.getIdentifier()); } catch (RemoteException re) { - Log.w(TAG, "Could not get application restrictions for user " + user.getIdentifier()); + throw re.rethrowAsRuntimeException(); } - return null; } /** @@ -1940,7 +1907,7 @@ public class UserManager { try { mService.setApplicationRestrictions(packageName, restrictions, user.getIdentifier()); } catch (RemoteException re) { - Log.w(TAG, "Could not set application restrictions for user " + user.getIdentifier()); + throw re.rethrowAsRuntimeException(); } } @@ -1964,7 +1931,7 @@ public class UserManager { try { mService.setDefaultGuestRestrictions(restrictions); } catch (RemoteException re) { - Log.w(TAG, "Could not set guest restrictions"); + throw re.rethrowAsRuntimeException(); } } @@ -1976,9 +1943,8 @@ public class UserManager { try { return mService.getDefaultGuestRestrictions(); } catch (RemoteException re) { - Log.w(TAG, "Could not set guest restrictions"); + throw re.rethrowAsRuntimeException(); } - return new Bundle(); } /** @@ -1991,8 +1957,7 @@ public class UserManager { try { return mService.getUserCreationTime(userHandle.getIdentifier()); } catch (RemoteException re) { - Log.w(TAG, "Could not get user creation time", re); - return 0; + throw re.rethrowAsRuntimeException(); } } @@ -2008,8 +1973,7 @@ public class UserManager { try { return mService.someUserHasSeedAccount(accountName, accountType); } catch (RemoteException re) { - Log.w(TAG, "Could not check seed accounts", re); - return false; + throw re.rethrowAsRuntimeException(); } } } diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 7b461b17329c..53c92bf6e7ce 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -929,26 +929,31 @@ public abstract class NotificationListenerService extends Service { public static final int IMPORTANCE_NONE = 0; /** - * Low notification importance: only shows in the shade, below the fold. + * Min notification importance: only shows in the shade, below the fold. */ - public static final int IMPORTANCE_LOW = 1; + public static final int IMPORTANCE_MIN = 1; /** - * Default notification importance: shows everywhere, but is not intrusive. + * Low notification importance: shows everywhere, but is not intrusive. */ - public static final int IMPORTANCE_DEFAULT = 2; + public static final int IMPORTANCE_LOW = 2; /** - * Higher notification importance: shows everywhere, makes noise, + * Default notification importance: shows everywhere, allowed to makes noise, * but does not visually intrude. */ - public static final int IMPORTANCE_HIGH = 3; + public static final int IMPORTANCE_DEFAULT = 3; /** - * Highest notification importance: shows everywhere, makes noise, - * and also visually intrudes. + * Higher notification importance: shows everywhere, allowed to makes noise and peek. */ - public static final int IMPORTANCE_MAX = 4; + public static final int IMPORTANCE_HIGH = 4; + + /** + * Highest notification importance: shows everywhere, allowed to makes noise, peek, and + * use full screen intents. + */ + public static final int IMPORTANCE_MAX = 5; private String mKey; private int mRank = -1; @@ -1041,7 +1046,7 @@ public abstract class NotificationListenerService extends Service { CharSequence explanation) { mKey = key; mRank = rank; - mIsAmbient = importance < IMPORTANCE_DEFAULT; + mIsAmbient = importance < IMPORTANCE_LOW; mMatchesInterruptionFilter = matchesInterruptionFilter; mVisibilityOverride = visibilityOverride; mSuppressedVisualEffects = suppressedVisualEffects; @@ -1058,6 +1063,8 @@ public abstract class NotificationListenerService extends Service { return "UNSPECIFIED"; case IMPORTANCE_NONE: return "NONE"; + case IMPORTANCE_MIN: + return "MIN"; case IMPORTANCE_LOW: return "LOW"; case IMPORTANCE_DEFAULT: diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 1cccfaeb7dc3..063288e2adfc 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -2563,6 +2563,8 @@ public class RemoteViews implements Parcelable, Filter { * @param format The Chronometer format string, or null to * simply display the timer value. * @param started True if you want the clock to be started, false if not. + * + * @see #setChronometerCountsDown(int, boolean) */ public void setChronometer(int viewId, long base, String format, boolean started) { setLong(viewId, "setBase", base); @@ -2571,6 +2573,18 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Equivalent to calling {@link Chronometer#setCountDown(boolean) Chronometer.setCountDown} on + * the chronometer with the given viewId. + * + * @param viewId The id of the {@link Chronometer} to change + * @param isCountDown True if you want the chronometer to count down to base instead of + * counting up. + */ + public void setChronometerCountsDown(int viewId, boolean isCountDown) { + setBoolean(viewId, "setCountDown", isCountDown); + } + + /** * Equivalent to calling {@link ProgressBar#setMax ProgressBar.setMax}, * {@link ProgressBar#setProgress ProgressBar.setProgress}, and * {@link ProgressBar#setIndeterminate ProgressBar.setIndeterminate} diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 4be365944982..a8d684d7a612 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -33,6 +33,7 @@ LOCAL_SRC_FILES:= \ com_android_internal_content_NativeLibraryHelper.cpp \ com_google_android_gles_jni_EGLImpl.cpp \ com_google_android_gles_jni_GLImpl.cpp.arm \ + android_app_ApplicationLoaders.cpp \ android_app_NativeActivity.cpp \ android_auditing_SecurityLog.cpp \ android_opengl_EGL14.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 017fb533723c..6ed07a7c9990 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -177,6 +177,7 @@ extern int register_android_backup_BackupDataOutput(JNIEnv *env); extern int register_android_backup_FileBackupHelperBase(JNIEnv *env); extern int register_android_backup_BackupHelperDispatcher(JNIEnv *env); extern int register_android_app_backup_FullBackup(JNIEnv *env); +extern int register_android_app_ApplicationLoaders(JNIEnv* env); extern int register_android_app_ActivityThread(JNIEnv *env); extern int register_android_app_NativeActivity(JNIEnv *env); extern int register_android_media_RemoteDisplay(JNIEnv *env); @@ -1371,6 +1372,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_backup_FileBackupHelperBase), REG_JNI(register_android_backup_BackupHelperDispatcher), REG_JNI(register_android_app_backup_FullBackup), + REG_JNI(register_android_app_ApplicationLoaders), REG_JNI(register_android_app_ActivityThread), REG_JNI(register_android_app_NativeActivity), REG_JNI(register_android_util_jar_StrictJarFile), diff --git a/core/jni/android_app_ApplicationLoaders.cpp b/core/jni/android_app_ApplicationLoaders.cpp new file mode 100644 index 000000000000..89f22eba0f30 --- /dev/null +++ b/core/jni/android_app_ApplicationLoaders.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2016 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. + */ + +#include <string> + +#include "nativeloader/native_loader.h" + +#include "core_jni_helpers.h" + + +static jstring createClassloaderNamespace_native(JNIEnv* env, + jobject clazz, + jobject classLoader, + jint targetSdkVersion, + jstring librarySearchPath, + jstring libraryPermittedPath, + jboolean isShared) { + return android::CreateClassLoaderNamespace(env, targetSdkVersion, + classLoader, isShared == JNI_TRUE, + librarySearchPath, libraryPermittedPath); +} + +static const JNINativeMethod g_methods[] = { + { "createClassloaderNamespace", + "(Ljava/lang/ClassLoader;ILjava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;", + reinterpret_cast<void*>(createClassloaderNamespace_native) }, +}; + +static const char* const kApplicationLoadersPathName = "android/app/ApplicationLoaders"; + +namespace android +{ + +int register_android_app_ApplicationLoaders(JNIEnv* env) { + return RegisterMethodsOrDie(env, kApplicationLoadersPathName, g_methods, NELEM(g_methods)); +} + +} // namespace android diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 88a56d2084d0..6431b94be4d3 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -259,8 +259,7 @@ static jlong loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName, jobject messageQueue, jstring internalDataDir, jstring obbDir, jstring externalDataDir, jint sdkVersion, jobject jAssetMgr, - jbyteArray savedState, jobject classLoader, jstring libraryPath, - jstring isolationPath) { + jbyteArray savedState, jobject classLoader, jstring libraryPath) { if (kLogTrace) { ALOGD("loadNativeCode_native"); } @@ -269,8 +268,7 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName std::unique_ptr<NativeCode> code; bool needNativeBridge = false; - void* handle = OpenNativeLibrary(env, sdkVersion, pathStr, classLoader, - false, libraryPath, isolationPath); + void* handle = OpenNativeLibrary(env, sdkVersion, pathStr, classLoader, libraryPath); if (handle == NULL) { if (NativeBridgeIsSupported(pathStr)) { handle = NativeBridgeLoadLibrary(pathStr, RTLD_LAZY); @@ -656,7 +654,7 @@ onContentRectChanged_native(JNIEnv* env, jobject clazz, jlong handle, static const JNINativeMethod g_methods[] = { { "loadNativeCode", - "(Ljava/lang/String;Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;[BLjava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/String;)J", + "(Ljava/lang/String;Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;[BLjava/lang/ClassLoader;Ljava/lang/String;)J", (void*)loadNativeCode_native }, { "getDlError", "()Ljava/lang/String;", (void*) getDlError_native }, { "unloadNativeCode", "(J)V", (void*)unloadNativeCode_native }, diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp index 6904fda1bef8..3e4e3522d5dd 100644 --- a/core/jni/android_media_AudioRecord.cpp +++ b/core/jni/android_media_AudioRecord.cpp @@ -181,57 +181,14 @@ static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioR static jint android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa, jintArray jSampleRate, jint channelMask, jint channelIndexMask, - jint audioFormat, jint buffSizeInBytes, jintArray jSession, jstring opPackageName) + jint audioFormat, jint buffSizeInBytes, jintArray jSession, jstring opPackageName, + jlong nativeRecordInJavaObj) { - jint elements[1]; - env->GetIntArrayRegion(jSampleRate, 0, 1, elements); - int sampleRateInHertz = elements[0]; - //ALOGV(">> Entering android_media_AudioRecord_setup"); - //ALOGV("sampleRate=%d, audioFormat=%d, channel mask=%x, buffSizeInBytes=%d", - // sampleRateInHertz, audioFormat, channelMask, buffSizeInBytes); - - if (jaa == 0) { - ALOGE("Error creating AudioRecord: invalid audio attributes"); - return (jint) AUDIO_JAVA_ERROR; - } - - // channel index mask takes priority over channel position masks. - if (channelIndexMask) { - // Java channel index masks need the representation bits set. - channelMask = audio_channel_mask_from_representation_and_bits( - AUDIO_CHANNEL_REPRESENTATION_INDEX, - channelIndexMask); - } - // Java channel position masks map directly to the native definition - - if (!audio_is_input_channel(channelMask)) { - ALOGE("Error creating AudioRecord: channel mask %#x is not valid.", channelMask); - return (jint) AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK; - } - uint32_t channelCount = audio_channel_count_from_in_mask(channelMask); - - // compare the format against the Java constants - audio_format_t format = audioFormatToNative(audioFormat); - if (format == AUDIO_FORMAT_INVALID) { - ALOGE("Error creating AudioRecord: unsupported audio format %d.", audioFormat); - return (jint) AUDIORECORD_ERROR_SETUP_INVALIDFORMAT; - } - - size_t bytesPerSample = audio_bytes_per_sample(format); - - if (buffSizeInBytes == 0) { - ALOGE("Error creating AudioRecord: frameCount is 0."); - return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT; - } - size_t frameSize = channelCount * bytesPerSample; - size_t frameCount = buffSizeInBytes / frameSize; - - jclass clazz = env->GetObjectClass(thiz); - if (clazz == NULL) { - ALOGE("Can't find %s when setting up callback.", kClassPathName); - return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED; - } + //ALOGV("sampleRate=%d, audioFormat=%d, channel mask=%x, buffSizeInBytes=%d " + // "nativeRecordInJavaObj=0x%llX", + // sampleRateInHertz, audioFormat, channelMask, buffSizeInBytes, nativeRecordInJavaObj); + audio_channel_mask_t localChanMask = inChannelMaskToNative(channelMask); if (jSession == NULL) { ALOGE("Error creating AudioRecord: invalid session ID pointer"); @@ -247,55 +204,132 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this, env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); nSession = NULL; - ScopedUtfChars opPackageNameStr(env, opPackageName); + audio_attributes_t *paa = NULL; + sp<AudioRecord> lpRecorder = 0; + audiorecord_callback_cookie *lpCallbackData = NULL; + + jclass clazz = env->GetObjectClass(thiz); + if (clazz == NULL) { + ALOGE("Can't find %s when setting up callback.", kClassPathName); + return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED; + } - // create an uninitialized AudioRecord object - sp<AudioRecord> lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str())); + // if we pass in an existing *Native* AudioRecord, we don't need to create/initialize one. + if (nativeRecordInJavaObj == 0) { + if (jaa == 0) { + ALOGE("Error creating AudioRecord: invalid audio attributes"); + return (jint) AUDIO_JAVA_ERROR; + } - audio_attributes_t *paa = NULL; - // read the AudioAttributes values - paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t)); - const jstring jtags = - (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags); - const char* tags = env->GetStringUTFChars(jtags, NULL); - // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it - strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1); - env->ReleaseStringUTFChars(jtags, tags); - paa->source = (audio_source_t) env->GetIntField(jaa, javaAudioAttrFields.fieldRecSource); - paa->flags = (audio_flags_mask_t)env->GetIntField(jaa, javaAudioAttrFields.fieldFlags); - ALOGV("AudioRecord_setup for source=%d tags=%s flags=%08x", paa->source, paa->tags, paa->flags); - - audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE; - if (paa->flags & AUDIO_FLAG_HW_HOTWORD) { - flags = AUDIO_INPUT_FLAG_HW_HOTWORD; - } - // create the callback information: - // this data will be passed with every AudioRecord callback - audiorecord_callback_cookie *lpCallbackData = new audiorecord_callback_cookie; - lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz); - // we use a weak reference so the AudioRecord object can be garbage collected. - lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this); - lpCallbackData->busy = false; - - const status_t status = lpRecorder->set(paa->source, - sampleRateInHertz, - format, // word length, PCM - channelMask, - frameCount, - recorderCallback,// callback_t - lpCallbackData,// void* user - 0, // notificationFrames, - true, // threadCanCallJava - sessionId, - AudioRecord::TRANSFER_DEFAULT, - flags, - -1, -1, // default uid, pid - paa); - - if (status != NO_ERROR) { - ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.", - status); - goto native_init_failure; + if (jSampleRate == 0) { + ALOGE("Error creating AudioRecord: invalid sample rates"); + return (jint) AUDIO_JAVA_ERROR; + } + jint elements[1]; + env->GetIntArrayRegion(jSampleRate, 0, 1, elements); + int sampleRateInHertz = elements[0]; + + // channel index mask takes priority over channel position masks. + if (channelIndexMask) { + // Java channel index masks need the representation bits set. + localChanMask = audio_channel_mask_from_representation_and_bits( + AUDIO_CHANNEL_REPRESENTATION_INDEX, + channelIndexMask); + } + // Java channel position masks map directly to the native definition + + if (!audio_is_input_channel(localChanMask)) { + ALOGE("Error creating AudioRecord: channel mask %#x is not valid.", localChanMask); + return (jint) AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK; + } + uint32_t channelCount = audio_channel_count_from_in_mask(localChanMask); + + // compare the format against the Java constants + audio_format_t format = audioFormatToNative(audioFormat); + if (format == AUDIO_FORMAT_INVALID) { + ALOGE("Error creating AudioRecord: unsupported audio format %d.", audioFormat); + return (jint) AUDIORECORD_ERROR_SETUP_INVALIDFORMAT; + } + + size_t bytesPerSample = audio_bytes_per_sample(format); + + if (buffSizeInBytes == 0) { + ALOGE("Error creating AudioRecord: frameCount is 0."); + return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT; + } + size_t frameSize = channelCount * bytesPerSample; + size_t frameCount = buffSizeInBytes / frameSize; + + ScopedUtfChars opPackageNameStr(env, opPackageName); + + // create an uninitialized AudioRecord object + lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str())); + + // read the AudioAttributes values + paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t)); + const jstring jtags = + (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags); + const char* tags = env->GetStringUTFChars(jtags, NULL); + // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it + strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1); + env->ReleaseStringUTFChars(jtags, tags); + paa->source = (audio_source_t) env->GetIntField(jaa, javaAudioAttrFields.fieldRecSource); + paa->flags = (audio_flags_mask_t)env->GetIntField(jaa, javaAudioAttrFields.fieldFlags); + ALOGV("AudioRecord_setup for source=%d tags=%s flags=%08x", paa->source, paa->tags, paa->flags); + + audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE; + if (paa->flags & AUDIO_FLAG_HW_HOTWORD) { + flags = AUDIO_INPUT_FLAG_HW_HOTWORD; + } + // create the callback information: + // this data will be passed with every AudioRecord callback + lpCallbackData = new audiorecord_callback_cookie; + lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz); + // we use a weak reference so the AudioRecord object can be garbage collected. + lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this); + lpCallbackData->busy = false; + + const status_t status = lpRecorder->set(paa->source, + sampleRateInHertz, + format, // word length, PCM + localChanMask, + frameCount, + recorderCallback,// callback_t + lpCallbackData,// void* user + 0, // notificationFrames, + true, // threadCanCallJava + sessionId, + AudioRecord::TRANSFER_DEFAULT, + flags, + -1, -1, // default uid, pid + paa); + + if (status != NO_ERROR) { + ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.", + status); + goto native_init_failure; + } + } else { // end if nativeRecordInJavaObj == 0) + lpRecorder = (AudioRecord*)nativeRecordInJavaObj; + // TODO: We need to find out which members of the Java AudioRecord might need to be + // initialized from the Native AudioRecord + // these are directly returned from getters: + // mSampleRate + // mRecordSource + // mAudioFormat + // mChannelMask + // mChannelCount + // mState (?) + // mRecordingState (?) + // mPreferredDevice + + // create the callback information: + // this data will be passed with every AudioRecord callback + lpCallbackData = new audiorecord_callback_cookie; + lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz); + // we use a weak reference so the AudioRecord object can be garbage collected. + lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this); + lpCallbackData->busy = false; } nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); @@ -726,8 +760,8 @@ static const JNINativeMethod gMethods[] = { // name, signature, funcPtr {"native_start", "(II)I", (void *)android_media_AudioRecord_start}, {"native_stop", "()V", (void *)android_media_AudioRecord_stop}, - {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILjava/lang/String;)I", - (void *)android_media_AudioRecord_setup}, + {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILjava/lang/String;J)I", + (void *)android_media_AudioRecord_setup}, {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize}, {"native_release", "()V", (void *)android_media_AudioRecord_release}, {"native_read_in_byte_array", diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index 84cc185d13e2..660cbdcb4546 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -213,55 +213,17 @@ static inline audio_channel_mask_t nativeChannelMaskFromJavaChannelMasks( // ---------------------------------------------------------------------------- static jint -android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, - jobject jaa, +android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa, jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask, - jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession) { + jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession, + jlong nativeAudioTrack) { - jint elements[1]; - env->GetIntArrayRegion(jSampleRate, 0, 1, elements); - int sampleRateInHertz = elements[0]; + ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d" + "nativeAudioTrack=0x%llX", + jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes, + nativeAudioTrack); - ALOGV("sampleRate=%d, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d", - sampleRateInHertz, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes); - - if (jaa == 0) { - ALOGE("Error creating AudioTrack: invalid audio attributes"); - return (jint) AUDIO_JAVA_ERROR; - } - - // Invalid channel representations are caught by !audio_is_output_channel() below. - audio_channel_mask_t nativeChannelMask = nativeChannelMaskFromJavaChannelMasks( - channelPositionMask, channelIndexMask); - if (!audio_is_output_channel(nativeChannelMask)) { - ALOGE("Error creating AudioTrack: invalid native channel mask %#x.", nativeChannelMask); - return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK; - } - - uint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask); - - // check the format. - // This function was called from Java, so we compare the format against the Java constants - audio_format_t format = audioFormatToNative(audioFormat); - if (format == AUDIO_FORMAT_INVALID) { - ALOGE("Error creating AudioTrack: unsupported audio format %d.", audioFormat); - return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT; - } - - // compute the frame count - size_t frameCount; - if (audio_has_proportional_frames(format)) { - const size_t bytesPerSample = audio_bytes_per_sample(format); - frameCount = buffSizeInBytes / (channelCount * bytesPerSample); - } else { - frameCount = buffSizeInBytes; - } - - jclass clazz = env->GetObjectClass(thiz); - if (clazz == NULL) { - ALOGE("Can't find %s when setting up callback.", kClassPathName); - return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; - } + sp<AudioTrack> lpTrack = 0; if (jSession == NULL) { ALOGE("Error creating AudioTrack: invalid session ID pointer"); @@ -277,91 +239,168 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); nSession = NULL; - // create the native AudioTrack object - sp<AudioTrack> lpTrack = new AudioTrack(); + AudioTrackJniStorage* lpJniStorage = NULL; audio_attributes_t *paa = NULL; - // read the AudioAttributes values - paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t)); - const jstring jtags = - (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags); - const char* tags = env->GetStringUTFChars(jtags, NULL); - // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it - strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1); - env->ReleaseStringUTFChars(jtags, tags); - paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage); - paa->content_type = - (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType); - paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags); - - ALOGV("AudioTrack_setup for usage=%d content=%d flags=0x%#x tags=%s", - paa->usage, paa->content_type, paa->flags, paa->tags); - - // initialize the callback information: - // this data will be passed with every AudioTrack callback - AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage(); - lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz); - // we use a weak reference so the AudioTrack object can be garbage collected. - lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this); - lpJniStorage->mCallbackData.busy = false; - - // initialize the native AudioTrack object - status_t status = NO_ERROR; - switch (memoryMode) { - case MODE_STREAM: - - status = lpTrack->set( - AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument) - sampleRateInHertz, - format,// word length, PCM - nativeChannelMask, - frameCount, - AUDIO_OUTPUT_FLAG_NONE, - audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user) - 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack - 0,// shared mem - true,// thread can call Java - sessionId,// audio session ID - AudioTrack::TRANSFER_SYNC, - NULL, // default offloadInfo - -1, -1, // default uid, pid values - paa); - break; - case MODE_STATIC: - // AudioTrack is using shared memory + jclass clazz = env->GetObjectClass(thiz); + if (clazz == NULL) { + ALOGE("Can't find %s when setting up callback.", kClassPathName); + return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; + } + + // if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one. + if (nativeAudioTrack == 0) { + if (jaa == 0) { + ALOGE("Error creating AudioTrack: invalid audio attributes"); + return (jint) AUDIO_JAVA_ERROR; + } - if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) { - ALOGE("Error creating AudioTrack in static mode: error creating mem heap base"); - goto native_init_failure; + if (jSampleRate == 0) { + ALOGE("Error creating AudioTrack: invalid sample rates"); + return (jint) AUDIO_JAVA_ERROR; } - status = lpTrack->set( - AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument) - sampleRateInHertz, - format,// word length, PCM - nativeChannelMask, - frameCount, - AUDIO_OUTPUT_FLAG_NONE, - audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)); - 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack - lpJniStorage->mMemBase,// shared mem - true,// thread can call Java - sessionId,// audio session ID - AudioTrack::TRANSFER_SHARED, - NULL, // default offloadInfo - -1, -1, // default uid, pid values - paa); - break; + int* sampleRates = env->GetIntArrayElements(jSampleRate, NULL); + int sampleRateInHertz = sampleRates[0]; + env->ReleaseIntArrayElements(jSampleRate, sampleRates, JNI_ABORT); - default: - ALOGE("Unknown mode %d", memoryMode); - goto native_init_failure; - } + // Invalid channel representations are caught by !audio_is_output_channel() below. + audio_channel_mask_t nativeChannelMask = nativeChannelMaskFromJavaChannelMasks( + channelPositionMask, channelIndexMask); + if (!audio_is_output_channel(nativeChannelMask)) { + ALOGE("Error creating AudioTrack: invalid native channel mask %#x.", nativeChannelMask); + return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK; + } - if (status != NO_ERROR) { - ALOGE("Error %d initializing AudioTrack", status); - goto native_init_failure; + uint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask); + + // check the format. + // This function was called from Java, so we compare the format against the Java constants + audio_format_t format = audioFormatToNative(audioFormat); + if (format == AUDIO_FORMAT_INVALID) { + ALOGE("Error creating AudioTrack: unsupported audio format %d.", audioFormat); + return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT; + } + + // compute the frame count + size_t frameCount; + if (audio_is_linear_pcm(format)) { + const size_t bytesPerSample = audio_bytes_per_sample(format); + frameCount = buffSizeInBytes / (channelCount * bytesPerSample); + } else { + frameCount = buffSizeInBytes; + } + + // create the native AudioTrack object + lpTrack = new AudioTrack(); + + // read the AudioAttributes values + paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t)); + const jstring jtags = + (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags); + const char* tags = env->GetStringUTFChars(jtags, NULL); + // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it + strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1); + env->ReleaseStringUTFChars(jtags, tags); + paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage); + paa->content_type = + (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType); + paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags); + + ALOGV("AudioTrack_setup for usage=%d content=%d flags=0x%#x tags=%s", + paa->usage, paa->content_type, paa->flags, paa->tags); + + // initialize the callback information: + // this data will be passed with every AudioTrack callback + lpJniStorage = new AudioTrackJniStorage(); + lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz); + // we use a weak reference so the AudioTrack object can be garbage collected. + lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this); + lpJniStorage->mCallbackData.busy = false; + + // initialize the native AudioTrack object + status_t status = NO_ERROR; + switch (memoryMode) { + case MODE_STREAM: + + status = lpTrack->set( + AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument) + sampleRateInHertz, + format,// word length, PCM + nativeChannelMask, + frameCount, + AUDIO_OUTPUT_FLAG_NONE, + audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user) + 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack + 0,// shared mem + true,// thread can call Java + sessionId,// audio session ID + AudioTrack::TRANSFER_SYNC, + NULL, // default offloadInfo + -1, -1, // default uid, pid values + paa); + break; + + case MODE_STATIC: + // AudioTrack is using shared memory + + if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) { + ALOGE("Error creating AudioTrack in static mode: error creating mem heap base"); + goto native_init_failure; + } + + status = lpTrack->set( + AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument) + sampleRateInHertz, + format,// word length, PCM + nativeChannelMask, + frameCount, + AUDIO_OUTPUT_FLAG_NONE, + audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)); + 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack + lpJniStorage->mMemBase,// shared mem + true,// thread can call Java + sessionId,// audio session ID + AudioTrack::TRANSFER_SHARED, + NULL, // default offloadInfo + -1, -1, // default uid, pid values + paa); + break; + + default: + ALOGE("Unknown mode %d", memoryMode); + goto native_init_failure; + } + + if (status != NO_ERROR) { + ALOGE("Error %d initializing AudioTrack", status); + goto native_init_failure; + } + } else { // end if (nativeAudioTrack == 0) + lpTrack = (AudioTrack*)nativeAudioTrack; + // TODO: We need to find out which members of the Java AudioTrack might + // need to be initialized from the Native AudioTrack + // these are directly returned from getters: + // mSampleRate + // mAudioFormat + // mStreamType + // mChannelConfiguration + // mChannelCount + // mState (?) + // mPlayState (?) + // these may be used internally (Java AudioTrack.audioParamCheck(): + // mChannelMask + // mChannelIndexMask + // mDataLoadMode + + // initialize the callback information: + // this data will be passed with every AudioTrack callback + lpJniStorage = new AudioTrackJniStorage(); + lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz); + // we use a weak reference so the AudioTrack object can be garbage collected. + lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this); + lpJniStorage->mCallbackData.busy = false; } nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); @@ -394,9 +433,11 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, // since we had audio attributes, the stream type was derived from them during the // creation of the native AudioTrack: push the same value to the Java object env->SetIntField(thiz, javaAudioTrackFields.fieldStreamType, (jint) lpTrack->streamType()); - // audio attributes were copied in AudioTrack creation - free(paa); - paa = NULL; + if (paa != NULL) { + // audio attributes were copied in AudioTrack creation + free(paa); + paa = NULL; + } return (jint) AUDIO_JAVA_SUCCESS; @@ -418,7 +459,6 @@ native_init_failure: return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; } - // ---------------------------------------------------------------------------- static void android_media_AudioTrack_start(JNIEnv *env, jobject thiz) @@ -1123,7 +1163,7 @@ static const JNINativeMethod gMethods[] = { {"native_stop", "()V", (void *)android_media_AudioTrack_stop}, {"native_pause", "()V", (void *)android_media_AudioTrack_pause}, {"native_flush", "()V", (void *)android_media_AudioTrack_flush}, - {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[I)I", + {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJ)I", (void *)android_media_AudioTrack_setup}, {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize}, {"native_release", "()V", (void *)android_media_AudioTrack_release}, diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 30d6f2929774..03d93a1720df 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -965,16 +965,6 @@ <!-- INSTALL PERMISSIONS --> <!-- ====================================================================== --> -` <!-- =========================================== --> - <!-- Permissions for accessing contact metadata --> - <!-- =========================================== --> - <eat-comment /> - - <!-- @SystemApi Allows an application to read/write contact metadata. - <p>Not for use by third-party applications. --> - <permission android:name="android.permission.READ_WRITE_CONTACT_METADATA" - android:protectionLevel="signature|system" /> - <!-- ================================== --> <!-- Permissions for accessing messages --> <!-- ================================== --> diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp index 00560d7df454..a14bdc4693e6 100644 --- a/libs/hwui/DisplayListCanvas.cpp +++ b/libs/hwui/DisplayListCanvas.cpp @@ -415,12 +415,7 @@ void DisplayListCanvas::drawPoints(const float* points, int count, const SkPaint void DisplayListCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { mDisplayList->ref(tree); - const SkBitmap& bitmap = tree->getBitmapUpdateIfDirty(); - SkPaint* paint = tree->getPaint(); - const SkRect bounds = tree->getBounds(); - addDrawOp(new (alloc()) DrawBitmapRectOp(refBitmap(bitmap), - 0, 0, bitmap.width(), bitmap.height(), - bounds.left(), bounds.top(), bounds.right(), bounds.bottom(), refPaint(paint))); + addDrawOp(new (alloc()) DrawVectorDrawableOp(tree)); } void DisplayListCanvas::drawTextOnPath(const uint16_t* glyphs, int count, diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index 98315d0a416a..516e6199bd27 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -28,6 +28,7 @@ #include "UvMapper.h" #include "utils/LinearAllocator.h" #include "utils/PaintUtils.h" +#include "VectorDrawable.h" #include <algorithm> @@ -1107,6 +1108,30 @@ private: float* mRadius; }; +class DrawVectorDrawableOp : public DrawOp { +public: + DrawVectorDrawableOp(VectorDrawableRoot* tree) + : DrawOp(nullptr), mTree(tree) {} + + virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { + const SkBitmap& bitmap = mTree->getBitmapUpdateIfDirty(); + SkPaint* paint = mTree->getPaint(); + const SkRect bounds = mTree->getBounds(); + renderer.drawBitmap(&bitmap, Rect(0, 0, bitmap.width(), bitmap.height()), + bounds, paint); + } + + virtual void output(int level, uint32_t logFlags) const override { + OP_LOG("Draw Vector Drawable %p", mTree); + } + + virtual const char* name() override { return "DrawVectorDrawable"; } + +private: + VectorDrawableRoot* mTree; + +}; + class DrawOvalOp : public DrawStrokableOp { public: DrawOvalOp(float left, float top, float right, float bottom, const SkPaint* paint) diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 8f6b17877fd4..b8f0717d7318 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -374,7 +374,7 @@ public class AudioRecord implements AudioRouting int initResult = native_setup( new WeakReference<AudioRecord>(this), mAudioAttributes, sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat, mNativeBufferSizeInBytes, - session, ActivityThread.currentOpPackageName()); + session, ActivityThread.currentOpPackageName(), 0 /*nativeRecordInJavaObj*/); if (initResult != SUCCESS) { loge("Error code "+initResult+" when initializing native AudioRecord object."); return; // with mState == STATE_UNINITIALIZED @@ -390,12 +390,32 @@ public class AudioRecord implements AudioRouting * A constructor which explicitly connects a Native (C++) AudioRecord. For use by * the AudioRecordRoutingProxy subclass. * @param nativeRecordInJavaObj A C/C++ pointer to a native AudioRecord - * (associated with an OpenSL ES recorder). + * (associated with an OpenSL ES recorder). Note: the caller must ensure a correct + * value here as no error checking is or can be done. */ /*package*/ AudioRecord(long nativeRecordInJavaObj) { - mNativeRecorderInJavaObj = nativeRecordInJavaObj; + int[] session = { 0 }; + int[] rates = { 0 }; + //TODO: update native initialization when information about hardware init failure + // due to capture device already open is available. + // Note that for this native_setup, we are providing an already created/initialized + // *Native* AudioRecord, so the attributes parameters to native_setup() are ignored. + int initResult = native_setup(new WeakReference<AudioRecord>(this), + null /*mAudioAttributes*/, + rates /*mSampleRates*/, + 0 /*mChannelMask*/, + 0 /*mChannelIndexMask*/, + 0 /*mAudioFormat*/, + 0 /*mNativeBufferSizeInBytes*/, + session, + ActivityThread.currentOpPackageName(), + nativeRecordInJavaObj); + if (initResult != SUCCESS) { + loge("Error code "+initResult+" when initializing native AudioRecord object."); + return; // with mState == STATE_UNINITIALIZED + } - // other initialization here... + mSessionId = session[0]; mState = STATE_INITIALIZED; } @@ -1712,7 +1732,8 @@ public class AudioRecord implements AudioRouting private native final int native_setup(Object audiorecord_this, Object /*AudioAttributes*/ attributes, int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat, - int buffSizeInBytes, int[] sessionId, String opPackageName); + int buffSizeInBytes, int[] sessionId, String opPackageName, + long nativeRecordInJavaObj); // TODO remove: implementation calls directly into implementation of native_release() private native final void native_finalize(); diff --git a/media/java/android/media/AudioRouting.java b/media/java/android/media/AudioRouting.java index 2161cf3ee7ab..41f92d49e39f 100644 --- a/media/java/android/media/AudioRouting.java +++ b/media/java/android/media/AudioRouting.java @@ -41,6 +41,14 @@ public interface AudioRouting { public AudioDeviceInfo getPreferredDevice(); /** + * Returns an {@link AudioDeviceInfo} identifying the current routing of this + * AudioTrack/AudioRecord. + * Note: The query is only valid if the AudioTrack/AudioRecord is currently playing. + * If it is not, <code>getRoutedDevice()</code> will return null. + */ + public AudioDeviceInfo getRoutedDevice(); + + /** * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing * changes on this AudioTrack/AudioRecord. * @param listener The {@link AudioRouting.OnRoutingChangedListener} interface to receive diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 708768cea19b..f02e8375f072 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -505,7 +505,7 @@ public class AudioTrack implements AudioRouting // native initialization int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes, sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat, - mNativeBufferSizeInBytes, mDataLoadMode, session); + mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/); if (initResult != SUCCESS) { loge("Error code "+initResult+" when initializing AudioTrack."); return; // with mState == STATE_UNINITIALIZED @@ -528,8 +528,6 @@ public class AudioTrack implements AudioRouting * (associated with an OpenSL ES player). */ /*package*/ AudioTrack(long nativeTrackInJavaObj) { - mNativeTrackInJavaObj = nativeTrackInJavaObj; - // "final"s mAttributes = null; mAppOps = null; @@ -542,6 +540,26 @@ public class AudioTrack implements AudioRouting mInitializationLooper = looper; // other initialization... + // Note that for this native_setup, we are providing an already created/initialized + // *Native* AudioTrack, so the attributes parameters to native_setup() are ignored. + int[] session = { 0 }; + int[] rates = { 0 }; + int initResult = native_setup(new WeakReference<AudioTrack>(this), + null /*mAttributes - NA*/, + rates /*sampleRate - NA*/, + 0 /*mChannelMask - NA*/, + 0 /*mChannelIndexMask - NA*/, + 0 /*mAudioFormat - NA*/, + 0 /*mNativeBufferSizeInBytes - NA*/, + 0 /*mDataLoadMode - NA*/, + session, + nativeTrackInJavaObj); + if (initResult != SUCCESS) { + loge("Error code "+initResult+" when initializing AudioTrack."); + return; // with mState == STATE_UNINITIALIZED + } + + mSessionId = session[0]; mState = STATE_INITIALIZED; } @@ -2773,7 +2791,7 @@ public class AudioTrack implements AudioRouting private native final int native_setup(Object /*WeakReference<AudioTrack>*/ audiotrack_this, Object /*AudioAttributes*/ attributes, int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat, - int buffSizeInBytes, int mode, int[] sessionId); + int buffSizeInBytes, int mode, int[] sessionId, long nativeAudioTrack); private native final void native_finalize(); diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java index 13db00ef3cd1..622900f5c7f9 100644 --- a/media/java/android/media/session/MediaController.java +++ b/media/java/android/media/session/MediaController.java @@ -592,10 +592,11 @@ public final class MediaController { } /** - * Request that the player prepare its playback. Once the preparation is done, the session - * will change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards, - * {@link #play} can be called to start playback. If the preparation is not needed, - * {@link #play} can be directly called without this method. + * Request that the player prepare its playback. In other words, other sessions can continue + * to play during the preparation of this session. This method can be used to speed up the + * start of the playback. Once the preparation is done, the session will change its playback + * state to {@link PlaybackState#STATE_PAUSED}. Afterwards, {@link #play} can be called to + * start playback. */ public void prepare() { try { @@ -606,10 +607,12 @@ public final class MediaController { } /** - * Request that the player prepare playback for a specific media id. Once the preparation is - * done, the session will change its playback state to {@link PlaybackState#STATE_PAUSED}. - * Afterwards, {@link #play} can be called to start playback. If the preparation is not - * needed, {@link #playFromMediaId} can be directly called without this method. + * Request that the player prepare playback for a specific media id. In other words, other + * sessions can continue to play during the preparation of this session. This method can be + * used to speed up the start of the playback. Once the preparation is done, the session + * will change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards, + * {@link #play} can be called to start playback. If the preparation is not needed, + * {@link #playFromMediaId} can be directly called without this method. * * @param mediaId The id of the requested media. * @param extras Optional extras that can include extra information about the media item @@ -628,12 +631,13 @@ public final class MediaController { } /** - * Request that the player prepare playback for a specific search query. - * An empty or null query should be treated as a request to prepare any - * music. Once the preparation is done, the session will change its playback state to - * {@link PlaybackState#STATE_PAUSED}. Afterwards, {@link #play} can be called to start - * playback. If the preparation is not needed, {@link #playFromSearch} can be directly - * called without this method. + * Request that the player prepare playback for a specific search query. An empty or null + * query should be treated as a request to prepare any music. In other words, other sessions + * can continue to play during the preparation of this session. This method can be used to + * speed up the start of the playback. Once the preparation is done, the session will + * change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards, + * {@link #play} can be called to start playback. If the preparation is not needed, + * {@link #playFromSearch} can be directly called without this method. * * @param query The search query. * @param extras Optional extras that can include extra information @@ -653,11 +657,12 @@ public final class MediaController { } /** - * Request that the player prepare playback for a specific {@link Uri}. - * Once the preparation is done, the session will change its playback state to - * {@link PlaybackState#STATE_PAUSED}. Afterwards, {@link #play} can be called to start - * playback. If the preparation is not needed, {@link #playFromUri} can be directly - * called without this method. + * Request that the player prepare playback for a specific {@link Uri}. In other words, + * other sessions can continue to play during the preparation of this session. This method + * can be used to speed up the start of the playback. Once the preparation is done, the + * session will change its playback state to {@link PlaybackState#STATE_PAUSED}. Afterwards, + * {@link #play} can be called to start playback. If the preparation is not needed, + * {@link #playFromUri} can be directly called without this method. * * @param uri The URI of the requested media. * @param extras Optional extras that can include extra information about the media item diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 9073077721fa..0bd1713c4a34 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -830,40 +830,45 @@ public final class MediaSession { } /** - * Override to handle requests to prepare playback. The state of playback should be updated - * to {@link PlaybackState#STATE_PAUSED} after the preparation is done. Override - * {@link #onPlay} to handle requests for starting playback of prepared content. + * Override to handle requests to prepare playback. During the preparation, a session should + * not hold audio focus in order to allow other sessions play seamlessly. The state of + * playback should be updated to {@link PlaybackState#STATE_PAUSED} after the preparation is + * done. */ public void onPrepare() { } /** * Override to handle requests to prepare for playing a specific mediaId that was provided - * by your app's {@link MediaBrowserService}. The state of playback should be updated - * to {@link PlaybackState#STATE_PAUSED} after the preparation is done. The playback of - * the prepared content should start in the implementation of {@link #onPlay}. Override - * {@link #onPlayFromMediaId} to handle requests for starting playback without preparation. + * by your app's {@link MediaBrowserService}. During the preparation, a session should not + * hold audio focus in order to allow other sessions play seamlessly. The state of playback + * should be updated to {@link PlaybackState#STATE_PAUSED} after the preparation is done. + * The playback of the prepared content should start in the implementation of + * {@link #onPlay}. Override {@link #onPlayFromMediaId} to handle requests for starting + * playback without preparation. */ public void onPrepareFromMediaId(String mediaId, Bundle extras) { } /** - * Override to handle requests to prepare playback from a search query. An - * empty query indicates that the app may prepare any music. The - * implementation should attempt to make a smart choice about what to - * play. The state of playback should be updated to {@link PlaybackState#STATE_PAUSED} - * after the preparation is done. The playback of the prepared content should start - * in the implementation of {@link #onPlay}. Override {@link #onPlayFromSearch} - * to handle requests for starting playback without preparation. + * Override to handle requests to prepare playback from a search query. An empty query + * indicates that the app may prepare any music. The implementation should attempt to make a + * smart choice about what to play. During the preparation, a session should not hold audio + * focus in order to allow other sessions play seamlessly. The state of playback should be + * updated to {@link PlaybackState#STATE_PAUSED} after the preparation is done. The playback + * of the prepared content should start in the implementation of {@link #onPlay}. Override + * {@link #onPlayFromSearch} to handle requests for starting playback without preparation. */ public void onPrepareFromSearch(String query, Bundle extras) { } /** * Override to handle requests to prepare a specific media item represented by a URI. - * The state of playback should be updated to {@link PlaybackState#STATE_PAUSED} - * after the preparation is done. The playback of the prepared content should start in - * the implementation of {@link #onPlay}. Override {@link #onPlayFromUri} to handle requests + * During the preparation, a session should not hold audio focus in order to allow + * other sessions play seamlessly. The state of playback should be updated to + * {@link PlaybackState#STATE_PAUSED} after the preparation is done. + * The playback of the prepared content should start in the implementation of + * {@link #onPlay}. Override {@link #onPlayFromUri} to handle requests * for starting playback without preparation. */ public void onPrepareFromUri(Uri uri, Bundle extras) { diff --git a/packages/DocumentsUI/res/values/config.xml b/packages/DocumentsUI/res/values/config.xml index 07498a026ac1..8a7654071a3e 100644 --- a/packages/DocumentsUI/res/values/config.xml +++ b/packages/DocumentsUI/res/values/config.xml @@ -19,7 +19,7 @@ <bool name="config_defaultAdvancedDevices">false</bool> <!-- Intentionally unset. Vendors should set this in an overlay. --> - <string name="trusted_quick_viewer_package"></string> + <string name="trusted_quick_viewer_package" translatable="false"></string> <bool name="list_divider_inset_left">true</bool> <bool name="always_show_summary">false</bool> </resources> diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml index b97918e7b9d1..3dc111aa9703 100644 --- a/packages/DocumentsUI/res/values/strings.xml +++ b/packages/DocumentsUI/res/values/strings.xml @@ -134,6 +134,8 @@ <string name="copy_notification_title">Copying files</string> <!-- Title of the move notification [CHAR LIMIT=24] --> <string name="move_notification_title">Moving files</string> + <!-- Title of the move notification [CHAR LIMIT=24] --> + <string name="delete_notification_title">Deleting files</string> <!-- Text shown on the copy notification to indicate remaining time, in minutes [CHAR LIMIT=24] --> <string name="copy_remaining"><xliff:g id="duration" example="3 minutes">%s</xliff:g> left</string> <!-- Toast shown when a file copy is kicked off --> @@ -206,4 +208,11 @@ <string name="allow">Allow</string> <!-- Text in the button asking user to deny access to a given directory. --> <string name="deny">Deny</string> + <!-- Dialog title shown to users when asking if they want to delete files (a confirmation). --> + <string name="delete_confirmation_title">Delete files?</string> + <!-- Dialog text shown to users when asking if they want to delete files (a confirmation). --> + <plurals name="delete_confirmation_message"> + <item quantity="one">Are you sure you want to delete <xliff:g id="count" example="1">%1$d</xliff:g> file?</item> + <item quantity="other">Are you sure you want to delete <xliff:g id="count" example="3">%1$d</xliff:g> files?</item> + </plurals> </resources> diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java index 90c2d1b606d5..6211085e71c7 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java @@ -31,6 +31,7 @@ import android.annotation.IntDef; import android.annotation.StringRes; import android.app.Activity; import android.app.ActivityManager; +import android.app.AlertDialog; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; @@ -38,6 +39,7 @@ import android.app.LoaderManager.LoaderCallbacks; import android.content.ClipData; import android.content.ContentResolver; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.Loader; import android.database.Cursor; @@ -149,9 +151,6 @@ public class DirectoryFragment extends Fragment private static final String TAG = "DirectoryFragment"; private static final int LOADER_ID = 42; - private static final int DELETE_UNDO_TIMEOUT = 5000; - private static final int DELETE_JOB_DELAY = 5500; - private static final int EMPTY_REVEAL_DURATION = 250; private Model mModel; private MultiSelectManager mSelectionManager; @@ -704,46 +703,28 @@ public class DirectoryFragment extends Fragment final DocumentInfo srcParent = getDisplayState().stack.peek(); new GetDocumentsTask() { @Override - void onDocumentsReady(List<DocumentInfo> docs) { - // Hide the files in the UI. - final SparseArray<String> hidden = mAdapter.hide(selected.getAll()); - - checkState(DELETE_JOB_DELAY > DELETE_UNDO_TIMEOUT); - String operationId = FileOperations.delete( - getActivity(), docs, srcParent, getDisplayState().stack, - DELETE_JOB_DELAY); - showDeleteSnackbar(hidden, operationId); - } - }.execute(selected); - } - - private void showDeleteSnackbar(final SparseArray<String> hidden, final String jobId) { - - Context context = getActivity(); - String message = Shared.getQuantityString(context, R.plurals.deleting, hidden.size()); - - // Show a snackbar informing the user that files will be deleted, and give them an option to - // cancel. - final Activity activity = getActivity(); - Snackbars.makeSnackbar(activity, message, DELETE_UNDO_TIMEOUT) - .setAction( - R.string.undo, - new View.OnClickListener() { - @Override - public void onClick(View view) {} - }) - .setCallback( - new Snackbar.Callback() { - @Override - public void onDismissed(Snackbar snackbar, int event) { - if (event == Snackbar.Callback.DISMISS_EVENT_ACTION) { - // If the delete was cancelled, just unhide the files. - FileOperations.cancel(activity, jobId); - mAdapter.unhide(hidden); - } + void onDocumentsReady(final List<DocumentInfo> docs) { + new AlertDialog.Builder(getActivity()) + .setTitle(R.string.delete_confirmation_title) + .setMessage( + Shared.getQuantityString( + getActivity(), + R.plurals.delete_confirmation_message, + docs.size())) + .setPositiveButton( + android.R.string.yes, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + // Hide the files in the UI. + mAdapter.hide(selected.getAll()); + FileOperations.delete( + getActivity(), docs, srcParent, getDisplayState().stack); } }) - .show(); + .setNegativeButton(android.R.string.no, null) + .show(); + } + }.execute(selected); } private void transferDocuments(final Selection selected, final @OpType int mode) { diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java b/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java index 844d07ad1721..faedd5e2d355 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java +++ b/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java @@ -67,12 +67,15 @@ import java.util.ArrayList; import java.util.List; class CopyJob extends Job { + private static final String TAG = "CopyJob"; - private static final int PROGRESS_INTERVAL_MILLIS = 1000; + private static final int PROGRESS_INTERVAL_MILLIS = 500; + final List<DocumentInfo> mSrcs; final ArrayList<DocumentInfo> convertedFiles = new ArrayList<>(); private long mStartTime = -1; + private long mBatchSize; private long mBytesCopied; private long mLastNotificationTime; @@ -496,8 +499,8 @@ class CopyJob extends Job { try { while ((len = in.read(buffer)) != -1) { if (isCanceled()) { - throw new ResourceException("Canceled copy mid-copy of %s", - src.derivedUri); + if (DEBUG) Log.d(TAG, "Canceled copy mid-copy of: " + src.derivedUri); + return; } out.write(buffer, 0, len); makeCopyProgress(len); diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java b/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java index 25bca4f3df25..8f451623cf72 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java +++ b/packages/DocumentsUI/src/com/android/documentsui/services/DeleteJob.java @@ -57,8 +57,8 @@ final class DeleteJob extends Job { @Override Builder createProgressBuilder() { return super.createProgressBuilder( - service.getString(R.string.move_notification_title), - R.drawable.ic_menu_copy, + service.getString(R.string.delete_notification_title), + R.drawable.ic_menu_delete, service.getString(android.R.string.cancel), R.drawable.ic_cab_cancel); } diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java index 9d017ee7943d..b53e1659dae3 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java +++ b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java @@ -22,7 +22,6 @@ import static com.android.documentsui.Shared.EXTRA_STACK; import static com.android.documentsui.Shared.asArrayList; import static com.android.documentsui.Shared.getQuantityString; import static com.android.documentsui.services.FileOperationService.EXTRA_CANCEL; -import static com.android.documentsui.services.FileOperationService.EXTRA_DELAY; import static com.android.documentsui.services.FileOperationService.EXTRA_JOB_ID; import static com.android.documentsui.services.FileOperationService.EXTRA_OPERATION; import static com.android.documentsui.services.FileOperationService.EXTRA_SRC_LIST; @@ -165,19 +164,16 @@ public final class FileOperations { * Use {@link #createJobId} if you don't have one handy. * @param srcDocs A list of src files to copy. * @param srcParent Parent of all the source documents. - * @param delay Number of milliseconds to wait before executing the job. * @return Id of the job. */ public static String delete( Activity activity, List<DocumentInfo> srcDocs, DocumentInfo srcParent, - DocumentStack location, int delay) { + DocumentStack location) { String jobId = createJobId(); - if (DEBUG) Log.d(TAG, "Initiating 'delete' operation id " + jobId - + " delayed by " + delay + " milliseconds."); + if (DEBUG) Log.d(TAG, "Initiating 'delete' operation id " + jobId + "."); Intent intent = createBaseIntent(OPERATION_DELETE, activity, jobId, srcDocs, srcParent, location); - intent.putExtra(EXTRA_DELAY, delay); activity.startService(intent); return jobId; diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/Job.java b/packages/DocumentsUI/src/com/android/documentsui/services/Job.java index afb3374699c9..a15865439f04 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/services/Job.java +++ b/packages/DocumentsUI/src/com/android/documentsui/services/Job.java @@ -201,8 +201,8 @@ abstract public class Job implements Runnable { } Notification getSetupNotification(String content) { - mProgressBuilder.setProgress(0, 0, true); - mProgressBuilder.setContentText(content); + mProgressBuilder.setProgress(0, 0, true) + .setContentText(content); return mProgressBuilder.build(); } @@ -221,6 +221,7 @@ abstract public class Job implements Runnable { .setCategory(Notification.CATEGORY_ERROR) .setSmallIcon(icon) .setAutoCancel(true); + return errorBuilder.build(); } diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java index 38e125704f76..498471e6828b 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java @@ -119,24 +119,23 @@ public class FilesActivityUiTest extends ActivityTest<FilesActivity> { device.waitForIdle(); bots.main.menuDelete().click(); - bots.directory.waitForDeleteSnackbar(); - bots.directory.assertDocumentsAbsent("file1.png"); + bots.main.findDialogOkButton().click(); - bots.directory.waitForDeleteSnackbarGone(); bots.directory.assertDocumentsAbsent("file1.png"); + } + + public void testDeleteDocument_Cancel() throws Exception { + initTestFiles(); - // Now delete from another root. - bots.roots.openRoot(ROOT_1_ID); + bots.roots.openRoot(ROOT_0_ID); - bots.directory.clickDocument("poodles.text"); + bots.directory.clickDocument("file1.png"); device.waitForIdle(); bots.main.menuDelete().click(); - bots.directory.waitForDeleteSnackbar(); - bots.directory.assertDocumentsAbsent("poodles.text"); + bots.main.findDialogCancelButton().click(); - bots.directory.waitForDeleteSnackbarGone(); - bots.directory.assertDocumentsAbsent("poodles.text"); + bots.directory.assertDocumentsPresent("file1.png"); } // Tests that pressing tab switches focus between the roots and directory listings. diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java index 2833418b11d6..5f33b32d7d54 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java @@ -80,7 +80,7 @@ public class RenameDocumentUiTest extends ActivityTest<FilesActivity> { bots.main.setDialogText(newName); device.waitForIdle(TIMEOUT); - bots.main.findRenameDialogOkButton().click(); + bots.main.findDialogOkButton().click(); device.waitForIdle(TIMEOUT); bots.directory.assertDocumentsAbsent(fileName1); @@ -108,7 +108,7 @@ public class RenameDocumentUiTest extends ActivityTest<FilesActivity> { bots.main.setDialogText(newName); device.waitForIdle(TIMEOUT); - bots.main.findRenameDialogCancelButton().click(); + bots.main.findDialogCancelButton().click(); device.waitForIdle(TIMEOUT); bots.directory.assertDocumentsPresent(fileName1); diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java index fe2a3c320987..c112867c14f9 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java @@ -168,12 +168,16 @@ public class UiBot extends BaseBot { return findObject("android:id/content", "android:id/text1"); } - public UiObject findRenameDialogOkButton() { - return findObject("android:id/content", "android:id/button1"); + public UiObject findDialogOkButton() { + UiObject object = findObject("android:id/content", "android:id/button1"); + object.waitForExists(mTimeout); + return object; } - public UiObject findRenameDialogCancelButton() { - return findObject("android:id/content", "android:id/button2"); + public UiObject findDialogCancelButton() { + UiObject object = findObject("android:id/content", "android:id/button2"); + object.waitForExists(mTimeout); + return object; } UiObject findMenuLabelWithName(String label) { diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java b/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java index 6a98405d5026..38435f4afa47 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java @@ -133,6 +133,8 @@ public class AppFuse { return mCallback.readObjectBytes(inode, offset, size, mBuffer); } catch (IOException e) { return -OsConstants.EIO; + } catch (UnsupportedOperationException e) { + return -OsConstants.ENOTSUP; } } diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java index 9578e6bd1d5e..4bed0039039f 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java @@ -89,7 +89,8 @@ class Mapper { * @return If roots are added or removed from the database. * @throws FileNotFoundException */ - synchronized boolean putStorageDocuments(String parentDocumentId, MtpRoot[] roots) + synchronized boolean putStorageDocuments( + String parentDocumentId, int[] operationsSupported, MtpRoot[] roots) throws FileNotFoundException { final SQLiteDatabase database = mDatabase.getSQLiteDatabase(); database.beginTransaction(); @@ -100,7 +101,11 @@ class Mapper { valuesList[i] = new ContentValues(); extraValuesList[i] = new ContentValues(); MtpDatabase.getStorageDocumentValues( - valuesList[i], extraValuesList[i], parentDocumentId, roots[i]); + valuesList[i], + extraValuesList[i], + parentDocumentId, + operationsSupported, + roots[i]); } final boolean changed = putDocuments( parentDocumentId, diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java index e14109abfe54..701543bf330c 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java @@ -689,9 +689,7 @@ class MtpDatabase { values.putNull(Document.COLUMN_SIZE); extraValues.clear(); - extraValues.put( - Root.COLUMN_FLAGS, - Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE); + extraValues.put(Root.COLUMN_FLAGS, getRootFlags(device.operationsSupported)); extraValues.putNull(Root.COLUMN_AVAILABLE_BYTES); extraValues.putNull(Root.COLUMN_CAPACITY_BYTES); extraValues.put(Root.COLUMN_MIME_TYPES, ""); @@ -700,12 +698,16 @@ class MtpDatabase { /** * Gets {@link ContentValues} for the given root. * @param values {@link ContentValues} that receives values. + * @param extraValues {@link ContentValues} that receives extra values for roots. + * @param parentDocumentId Parent document ID. + * @param supportedOperations Array of Operation code supported by the device. * @param root Root to be converted {@link ContentValues}. */ static void getStorageDocumentValues( ContentValues values, ContentValues extraValues, String parentDocumentId, + int[] operationsSupported, MtpRoot root) { values.clear(); values.put(COLUMN_DEVICE_ID, root.mDeviceId); @@ -722,9 +724,7 @@ class MtpDatabase { values.put(Document.COLUMN_FLAGS, 0); values.put(Document.COLUMN_SIZE, root.mMaxCapacity - root.mFreeSpace); - extraValues.put( - Root.COLUMN_FLAGS, - Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE); + extraValues.put(Root.COLUMN_FLAGS, getRootFlags(operationsSupported)); extraValues.put(Root.COLUMN_AVAILABLE_BYTES, root.mFreeSpace); extraValues.put(Root.COLUMN_CAPACITY_BYTES, root.mMaxCapacity); extraValues.put(Root.COLUMN_MIME_TYPES, ""); @@ -785,6 +785,14 @@ class MtpDatabase { return "application/octet-stream"; } + private static int getRootFlags(int[] operationsSupported) { + int rootFlag = Root.FLAG_SUPPORTS_IS_CHILD; + if (MtpDeviceRecord.isWritingSupported(operationsSupported)) { + rootFlag |= Root.FLAG_SUPPORTS_CREATE; + } + return rootFlag; + } + static String[] strings(Object... args) { final String[] results = new String[args.length]; for (int i = 0; i < args.length; i++) { diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java index 71716bd849ad..393c4de89ea4 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java @@ -17,6 +17,7 @@ package com.android.mtp; import android.annotation.Nullable; +import android.mtp.MtpConstants; class MtpDeviceRecord { public final int deviceId; @@ -38,4 +39,29 @@ class MtpDeviceRecord { this.operationsSupported = operationsSupported; this.eventsSupported = eventsSupported; } + + /** + * Helper method to check operations/events are supported by the device or not. + */ + static boolean isSupported(@Nullable int[] supportedList, int code) { + if (supportedList == null) { + return false; + } + for (int i = 0; i < supportedList.length; i++) { + if (supportedList[i] == code) { + return true; + } + } + return false; + } + + static boolean isPartialReadSupported(@Nullable int[] supportedList, long fileSize) { + return fileSize <= 0xffffffffl && + isSupported(supportedList, MtpConstants.OPERATION_GET_PARTIAL_OBJECT); + } + + static boolean isWritingSupported(@Nullable int[] supportedList) { + return isSupported(supportedList, MtpConstants.OPERATION_SEND_OBJECT_INFO) && + isSupported(supportedList, MtpConstants.OPERATION_SEND_OBJECT); + } } diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java index 48499787c3b9..a1c5c9bde372 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java @@ -201,6 +201,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { final Identifier identifier = mDatabase.createIdentifier(documentId); try { openDevice(identifier.mDeviceId); + final MtpDeviceRecord device = getDeviceToolkit(identifier.mDeviceId).mDeviceRecord; switch (mode) { case "r": final long fileSize = getFileSize(documentId); @@ -208,7 +209,8 @@ public class MtpDocumentsProvider extends DocumentsProvider { // 4GB. Fallback to non-seekable file descriptor. // TODO: Use getPartialObject64 for MTP devices that support Android vendor // extension. - if (fileSize <= 0xffffffffl) { + if (MtpDeviceRecord.isPartialReadSupported( + device.operationsSupported, fileSize)) { return mAppFuse.openFile(Integer.parseInt(documentId)); } else { return getPipeManager(identifier).readDocument(mMtpManager, identifier); @@ -216,8 +218,13 @@ public class MtpDocumentsProvider extends DocumentsProvider { case "w": // TODO: Clear the parent document loader task (if exists) and call notify // when writing is completed. - return getPipeManager(identifier).writeDocument( - getContext(), mMtpManager, identifier); + if (MtpDeviceRecord.isWritingSupported(device.operationsSupported)) { + return getPipeManager(identifier).writeDocument( + getContext(), mMtpManager, identifier); + } else { + throw new UnsupportedOperationException( + "The device does not support writing operation."); + } case "rw": // TODO: Add support for "rw" mode. throw new UnsupportedOperationException( @@ -290,6 +297,10 @@ public class MtpDocumentsProvider extends DocumentsProvider { try { final Identifier parentId = mDatabase.createIdentifier(parentDocumentId); openDevice(parentId.mDeviceId); + final MtpDeviceRecord record = getDeviceToolkit(parentId.mDeviceId).mDeviceRecord; + if (!MtpDeviceRecord.isWritingSupported(record.operationsSupported)) { + throw new UnsupportedOperationException(); + } final ParcelFileDescriptor pipe[] = ParcelFileDescriptor.createReliablePipe(); pipe[0].close(); // 0 bytes for a new document. final int formatCode = Document.MIME_TYPE_DIR.equals(mimeType) ? @@ -323,9 +334,9 @@ public class MtpDocumentsProvider extends DocumentsProvider { if (DEBUG) { Log.d(TAG, "Open device " + deviceId); } - mMtpManager.openDevice(deviceId); + final MtpDeviceRecord device = mMtpManager.openDevice(deviceId); final DeviceToolkit toolkit = - new DeviceToolkit(deviceId, mMtpManager, mResolver, mDatabase); + new DeviceToolkit(deviceId, mMtpManager, mResolver, mDatabase, device); mDeviceToolkits.put(deviceId, toolkit); mIntentSender.sendUpdateNotificationIntent(); try { @@ -347,20 +358,15 @@ public class MtpDocumentsProvider extends DocumentsProvider { mIntentSender.sendUpdateNotificationIntent(); } - int[] getOpenedDeviceIds() { + MtpDeviceRecord[] getOpenedDeviceRecordsCache() { synchronized (mDeviceListLock) { - return mMtpManager.getOpenedDeviceIds(); - } - } - - String getDeviceName(int deviceId) throws IOException { - synchronized (mDeviceListLock) { - for (final MtpDeviceRecord device : mMtpManager.getDevices()) { - if (device.deviceId == deviceId) { - return device.name; - } + final MtpDeviceRecord[] records = new MtpDeviceRecord[mDeviceToolkits.size()]; + int i = 0; + for (final DeviceToolkit toolkit : mDeviceToolkits.values()) { + records[i] = toolkit.mDeviceRecord; + i++; } - throw new IOException("Not found the device: " + Integer.toString(deviceId)); + return records; } } @@ -391,7 +397,10 @@ public class MtpDocumentsProvider extends DocumentsProvider { public void shutdown() { synchronized (mDeviceListLock) { try { - for (final int id : mMtpManager.getOpenedDeviceIds()) { + // Copy the opened key set because it will be modified when closing devices. + final Integer[] keySet = + mDeviceToolkits.keySet().toArray(new Integer[mDeviceToolkits.size()]); + for (final int id : keySet) { closeDeviceInternal(id); } } catch (InterruptedException|IOException e) { @@ -432,7 +441,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { getDeviceToolkit(deviceId).mDocumentLoader.close(); mDeviceToolkits.remove(deviceId); mMtpManager.closeDevice(deviceId); - if (getOpenedDeviceIds().length == 0) { + if (mDeviceToolkits.size() == 0) { mRootScanner.pause(); } } @@ -488,11 +497,14 @@ public class MtpDocumentsProvider extends DocumentsProvider { private static class DeviceToolkit { public final PipeManager mPipeManager; public final DocumentLoader mDocumentLoader; + public final MtpDeviceRecord mDeviceRecord; public DeviceToolkit( - int deviceId, MtpManager manager, ContentResolver resolver, MtpDatabase database) { + int deviceId, MtpManager manager, ContentResolver resolver, MtpDatabase database, + MtpDeviceRecord record) { mPipeManager = new PipeManager(database); mDocumentLoader = new DocumentLoader(deviceId, manager, resolver, database); + mDeviceRecord = record; } } @@ -501,8 +513,13 @@ public class MtpDocumentsProvider extends DocumentsProvider { public long readObjectBytes( int inode, long offset, long size, byte[] buffer) throws IOException { final Identifier identifier = mDatabase.createIdentifier(Integer.toString(inode)); - return mMtpManager.getPartialObject( - identifier.mDeviceId, identifier.mObjectHandle, offset, size, buffer); + final MtpDeviceRecord record = getDeviceToolkit(identifier.mDeviceId).mDeviceRecord; + if (MtpDeviceRecord.isPartialReadSupported(record.operationsSupported, offset)) { + return mMtpManager.getPartialObject( + identifier.mDeviceId, identifier.mObjectHandle, offset, size, buffer); + } else { + throw new UnsupportedOperationException(); + } } @Override diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java index 9c4952bc5759..9b42b7828893 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java @@ -67,38 +67,25 @@ public class MtpDocumentsService extends Service { */ private boolean updateForegroundState() { final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance(); - final int[] deviceIds = provider.getOpenedDeviceIds(); int notificationId = 0; Notification notification = null; // TODO: Hide notification if the device has already been removed. - for (final int deviceId : deviceIds) { - try { - final String title = getResources().getString( - R.string.accessing_notification_title, - provider.getDeviceName(deviceIds[0])); - final String description = getResources().getString( - R.string.accessing_notification_description); - notificationId = deviceId; - notification = new Notification.Builder(this) - .setLocalOnly(true) - .setContentTitle(title) - .setContentText(description) - .setSmallIcon(com.android.internal.R.drawable.stat_sys_data_usb) - .setCategory(Notification.CATEGORY_SYSTEM) - .setPriority(Notification.PRIORITY_LOW) - .build(); - mNotificationManager.notify(deviceId, notification); - } catch (IOException exp) { - logErrorMessage(exp); - // If we failed to obtain device name, it looks the device is unusable. - // Because this is the last device we opened, we should hide the notification - // for the case. - try { - provider.closeDevice(deviceIds[0]); - } catch (IOException | InterruptedException closeError) { - logErrorMessage(closeError); - } - } + for (final MtpDeviceRecord record : provider.getOpenedDeviceRecordsCache()) { + final String title = getResources().getString( + R.string.accessing_notification_title, + record.name); + final String description = getResources().getString( + R.string.accessing_notification_description); + notificationId = record.deviceId; + notification = new Notification.Builder(this) + .setLocalOnly(true) + .setContentTitle(title) + .setContentText(description) + .setSmallIcon(com.android.internal.R.drawable.stat_sys_data_usb) + .setCategory(Notification.CATEGORY_SYSTEM) + .setPriority(Notification.PRIORITY_LOW) + .build(); + mNotificationManager.notify(record.deviceId, notification); } if (notification != null) { diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java index 37dc7616b510..c49005f2beed 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java @@ -65,16 +65,14 @@ class MtpManager { */ private static final int PROTOCOL_MTP = 0; - private final UsbManager mManager; - // TODO: Save and restore the set of opened device. private final SparseArray<MtpDevice> mDevices = new SparseArray<>(); MtpManager(Context context) { mManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); } - synchronized void openDevice(int deviceId) throws IOException { + synchronized MtpDeviceRecord openDevice(int deviceId) throws IOException { UsbDevice rawDevice = null; for (final UsbDevice candidate : mManager.getDeviceList().values()) { if (candidate.getDeviceId() == deviceId) { @@ -113,6 +111,8 @@ class MtpManager { } mDevices.put(deviceId, device); + + return createDeviceRecord(rawDevice); } synchronized void closeDevice(int deviceId) throws IOException { @@ -126,45 +126,11 @@ class MtpManager { if (!isMtpDevice(device)) { continue; } - final MtpDevice mtpDevice = mDevices.get(device.getDeviceId()); - final boolean opened = mtpDevice != null; - final String name = device.getProductName(); - MtpRoot[] roots; - int[] operationsSupported = null; - int[] eventsSupported = null; - if (opened) { - try { - roots = getRoots(device.getDeviceId()); - } catch (IOException exp) { - Log.e(MtpDocumentsProvider.TAG, "Failed to open device", exp); - // If we failed to fetch roots for the device, we still returns device model - // with an empty set of roots so that the device is shown DocumentsUI as long as - // the device is physically connected. - roots = new MtpRoot[0]; - } - final MtpDeviceInfo info = mtpDevice.getDeviceInfo(); - if (info != null) { - operationsSupported = mtpDevice.getDeviceInfo().getOperationsSupported(); - eventsSupported = mtpDevice.getDeviceInfo().getEventsSupported(); - } - } else { - roots = new MtpRoot[0]; - } - devices.add(new MtpDeviceRecord( - device.getDeviceId(), name, device.getSerialNumber(), opened, roots, - operationsSupported, eventsSupported)); + devices.add(createDeviceRecord(device)); } return devices.toArray(new MtpDeviceRecord[devices.size()]); } - synchronized int[] getOpenedDeviceIds() { - final int[] result = new int[mDevices.size()]; - for (int i = 0; i < result.length; i++) { - result[i] = mDevices.keyAt(i); - } - return result; - } - MtpObjectInfo getObjectInfo(int deviceId, int objectHandle) throws IOException { final MtpDevice device = getDevice(deviceId); @@ -281,6 +247,36 @@ class MtpManager { } } + private MtpDeviceRecord createDeviceRecord(UsbDevice device) { + final MtpDevice mtpDevice = mDevices.get(device.getDeviceId()); + final boolean opened = mtpDevice != null; + final String name = device.getProductName(); + MtpRoot[] roots; + int[] operationsSupported = null; + int[] eventsSupported = null; + if (opened) { + try { + roots = getRoots(device.getDeviceId()); + } catch (IOException exp) { + Log.e(MtpDocumentsProvider.TAG, "Failed to open device", exp); + // If we failed to fetch roots for the device, we still returns device model + // with an empty set of roots so that the device is shown DocumentsUI as long as + // the device is physically connected. + roots = new MtpRoot[0]; + } + final MtpDeviceInfo info = mtpDevice.getDeviceInfo(); + if (info != null) { + operationsSupported = mtpDevice.getDeviceInfo().getOperationsSupported(); + eventsSupported = mtpDevice.getDeviceInfo().getEventsSupported(); + } + } else { + roots = new MtpRoot[0]; + } + return new MtpDeviceRecord( + device.getDeviceId(), name, device.getSerialNumber(), opened, roots, + operationsSupported, eventsSupported); + } + static boolean isMtpDevice(UsbDevice device) { for (int i = 0; i < device.getInterfaceCount(); i++) { final UsbInterface usbInterface = device.getInterface(i); diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java index a48bf12b308f..82ba21f659d1 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java @@ -149,7 +149,8 @@ final class RootScanner { } try { mDatabase.getMapper().startAddingDocuments(documentId); - if (mDatabase.getMapper().putStorageDocuments(documentId, device.roots)) { + if (mDatabase.getMapper().putStorageDocuments( + documentId, device.eventsSupported, device.roots)) { changed = true; } if (mDatabase.getMapper().stopAddingDocuments(documentId)) { diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java index b75a9e63a17e..a000895ab056 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java @@ -48,7 +48,7 @@ public class DocumentLoaderTest extends AndroidTestCase { mDatabase.getMapper().stopAddingDocuments(null); mDatabase.getMapper().startAddingDocuments("1"); - mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] { + mDatabase.getMapper().putStorageDocuments("1", new int[0], new MtpRoot[] { new MtpRoot(0, 0, "Storage", 1000, 1000, "") }); mDatabase.getMapper().stopAddingDocuments("1"); diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java index 05c9c57e2409..48cde4c18c26 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java @@ -30,10 +30,11 @@ import java.io.FileNotFoundException; import static android.provider.DocumentsContract.Document.*; import static com.android.mtp.MtpDatabase.strings; import static com.android.mtp.MtpDatabaseConstants.*; +import static com.android.mtp.TestUtil.OPERATIONS_SUPPORTED; @SmallTest public class MtpDatabaseTest extends AndroidTestCase { - private final String[] COLUMN_NAMES = new String[] { + private static final String[] COLUMN_NAMES = new String[] { DocumentsContract.Document.COLUMN_DOCUMENT_ID, MtpDatabaseConstants.COLUMN_DEVICE_ID, MtpDatabaseConstants.COLUMN_STORAGE_ID, @@ -75,13 +76,10 @@ public class MtpDatabaseTest extends AndroidTestCase { } public void testPutSingleStorageDocuments() throws Exception { - mDatabase.getMapper().startAddingDocuments(null); - mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord( - 0, "Device", null /* deviceKey */, true, new MtpRoot[0], null, null)); - mDatabase.getMapper().stopAddingDocuments(null); + addTestDevice(); mDatabase.getMapper().startAddingDocuments("1"); - mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] { + mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] { new MtpRoot(0, 1, "Storage", 1000, 2000, "") }); mDatabase.getMapper().stopAddingDocuments("1"); @@ -143,7 +141,7 @@ public class MtpDatabaseTest extends AndroidTestCase { addTestDevice(); mDatabase.getMapper().startAddingDocuments("1"); - mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] { + mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] { new MtpRoot(0, 1, "Storage", 1000, 2000, ""), new MtpRoot(0, 2, "Storage", 2000, 4000, ""), new MtpRoot(0, 3, "/@#%&<>Storage", 3000, 6000,"") @@ -273,7 +271,7 @@ public class MtpDatabaseTest extends AndroidTestCase { // Add device and two storages. addTestDevice(); mDatabase.getMapper().startAddingDocuments("1"); - mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] { + mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] { new MtpRoot(0, 100, "Storage A", 1000, 0, ""), new MtpRoot(0, 101, "Storage B", 1001, 0, "") }); @@ -304,7 +302,7 @@ public class MtpDatabaseTest extends AndroidTestCase { // Add two storages, but one's name is different from previous one. mDatabase.getMapper().startAddingDocuments("1"); - mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] { + mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] { new MtpRoot(0, 200, "Storage A", 2000, 0, ""), new MtpRoot(0, 202, "Storage C", 2002, 0, "") }); @@ -398,10 +396,10 @@ public class MtpDatabaseTest extends AndroidTestCase { mDatabase.getMapper().startAddingDocuments("1"); mDatabase.getMapper().startAddingDocuments("2"); - mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] { + mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] { new MtpRoot(0, 100, "Storage", 0, 0, "") }); - mDatabase.getMapper().putStorageDocuments("2", new MtpRoot[] { + mDatabase.getMapper().putStorageDocuments("2", OPERATIONS_SUPPORTED, new MtpRoot[] { new MtpRoot(1, 100, "Storage", 0, 0, "") }); @@ -442,10 +440,10 @@ public class MtpDatabaseTest extends AndroidTestCase { mDatabase.getMapper().startAddingDocuments("1"); mDatabase.getMapper().startAddingDocuments("2"); - mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] { + mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] { new MtpRoot(0, 200, "Storage", 2000, 0, "") }); - mDatabase.getMapper().putStorageDocuments("2", new MtpRoot[] { + mDatabase.getMapper().putStorageDocuments("2", OPERATIONS_SUPPORTED, new MtpRoot[] { new MtpRoot(1, 300, "Storage", 3000, 0, "") }); mDatabase.getMapper().stopAddingDocuments("1"); @@ -562,7 +560,7 @@ public class MtpDatabaseTest extends AndroidTestCase { addTestDevice(); mDatabase.getMapper().startAddingDocuments("1"); - mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] { + mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] { new MtpRoot(0, 100, "Storage", 0, 0, ""), }); mDatabase.getMapper().clearMapping(); @@ -576,7 +574,7 @@ public class MtpDatabaseTest extends AndroidTestCase { } mDatabase.getMapper().startAddingDocuments("1"); - mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] { + mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] { new MtpRoot(0, 200, "Storage", 2000, 0, ""), }); mDatabase.getMapper().clearMapping(); @@ -584,7 +582,7 @@ public class MtpDatabaseTest extends AndroidTestCase { addTestDevice(); mDatabase.getMapper().startAddingDocuments("1"); - mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] { + mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] { new MtpRoot(0, 300, "Storage", 3000, 0, ""), }); mDatabase.getMapper().stopAddingDocuments("1"); @@ -625,7 +623,7 @@ public class MtpDatabaseTest extends AndroidTestCase { // Add a device and two storages that has same name. addTestDevice(); mDatabase.getMapper().startAddingDocuments("1"); - mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] { + mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] { new MtpRoot(0, 200, "Storage", 2000, 0, ""), new MtpRoot(0, 201, "Storage", 2001, 0, ""), }); @@ -658,13 +656,13 @@ public class MtpDatabaseTest extends AndroidTestCase { // The client code should be able to replace existing rows with new information. // Add one. mDatabase.getMapper().startAddingDocuments("1"); - mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] { + mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] { new MtpRoot(0, 100, "Storage A", 0, 0, ""), }); mDatabase.getMapper().stopAddingDocuments("1"); // Replace it. mDatabase.getMapper().startAddingDocuments("1"); - mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] { + mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] { new MtpRoot(0, 100, "Storage B", 1000, 1000, ""), }); mDatabase.getMapper().stopAddingDocuments("1"); @@ -703,7 +701,7 @@ public class MtpDatabaseTest extends AndroidTestCase { // Add one. addTestDevice(); mDatabase.getMapper().startAddingDocuments("1"); - mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] { + mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] { new MtpRoot(0, 100, "Storage A", 0, 0, ""), }); mDatabase.getMapper().clearMapping(); @@ -717,11 +715,11 @@ public class MtpDatabaseTest extends AndroidTestCase { // Add one. mDatabase.getMapper().startAddingDocuments("1"); - mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] { + mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] { new MtpRoot(0, 101, "Storage B", 1000, 1000, ""), }); // Add one more before resolving unmapped documents. - mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] { + mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] { new MtpRoot(0, 102, "Storage B", 1000, 1000, ""), }); mDatabase.getMapper().stopAddingDocuments("1"); @@ -763,7 +761,7 @@ public class MtpDatabaseTest extends AndroidTestCase { } mDatabase.getMapper().startAddingDocuments("1"); - mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] { + mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] { new MtpRoot(0, 100, "Storage A", 0, 0, "") }); mDatabase.getMapper().stopAddingDocuments("1"); @@ -778,7 +776,7 @@ public class MtpDatabaseTest extends AndroidTestCase { } mDatabase.getMapper().startAddingDocuments("1"); - mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] { + mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] { new MtpRoot(0, 100, "Storage A", 0, 0, ""), new MtpRoot(0, 101, "Storage B", 0, 0, "") }); @@ -798,7 +796,7 @@ public class MtpDatabaseTest extends AndroidTestCase { addTestDevice(); mDatabase.getMapper().startAddingDocuments("1"); - mDatabase.getMapper().putStorageDocuments("1", new MtpRoot[] { + mDatabase.getMapper().putStorageDocuments("1", OPERATIONS_SUPPORTED, new MtpRoot[] { new MtpRoot(0, 100, "Storage A", 0, 0, ""), }); mDatabase.getMapper().stopAddingDocuments("1"); diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java index 884d132d2b42..db82bcba97e3 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java @@ -38,6 +38,7 @@ import java.util.Arrays; import java.util.concurrent.TimeoutException; import static com.android.mtp.MtpDatabase.strings; +import static com.android.mtp.TestUtil.OPERATIONS_SUPPORTED; @MediumTest public class MtpDocumentsProviderTest extends AndroidTestCase { @@ -77,7 +78,7 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { 2048 /* total space */, "" /* no volume identifier */) }, - null, + OPERATIONS_SUPPORTED, null)); mProvider.resumeRootScanner(); @@ -98,7 +99,7 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { } catch (Throwable error) { assertTrue(error instanceof IOException); } - assertEquals(0, mProvider.getOpenedDeviceIds().length); + assertEquals(0, mProvider.getOpenedDeviceRecordsCache().length); // Check if the following notification is the first one or not. mMtpManager.addValidDevice(new MtpDeviceRecord( @@ -115,7 +116,7 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { 2048 /* total space */, "" /* no volume identifier */) }, - null, + OPERATIONS_SUPPORTED, null)); mProvider.resumeRootScanner(); mResolver.waitForNotification(ROOTS_URI, 1); @@ -139,7 +140,7 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { 2048 /* total space */, "" /* no volume identifier */) }, - null, + OPERATIONS_SUPPORTED, null)); mMtpManager.setObjectHandles(0, 1, -1, new int[0]); mProvider.resumeRootScanner(); @@ -155,16 +156,16 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { assertEquals(1, cursor.getLong(1)); } { - final int [] openedDevice = mProvider.getOpenedDeviceIds(); + final MtpDeviceRecord[] openedDevice = mProvider.getOpenedDeviceRecordsCache(); assertEquals(0, openedDevice.length); } // Device is opened automatically when querying its children. try (final Cursor cursor = mProvider.queryChildDocuments("1", null, null)) {} { - final int [] openedDevice = mProvider.getOpenedDeviceIds(); + final MtpDeviceRecord[] openedDevice = mProvider.getOpenedDeviceRecordsCache(); assertEquals(1, openedDevice.length); - assertEquals(0, openedDevice[0]); + assertEquals(0, openedDevice[0].deviceId); } } @@ -184,7 +185,7 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { 2048 /* total space */, "" /* no volume identifier */) }, - null, + OPERATIONS_SUPPORTED, null)); mMtpManager.addValidDevice(new MtpDeviceRecord( 1, @@ -200,7 +201,7 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { 4096 /* total space */, "Identifier B" /* no volume identifier */) }, - null, + new int[0] /* No operations supported */, null)); { @@ -225,7 +226,7 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { cursor.moveToNext(); cursor.moveToNext(); assertEquals("2", cursor.getString(0)); - assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1)); + assertEquals(Root.FLAG_SUPPORTS_IS_CHILD, cursor.getInt(1)); assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2)); assertEquals("Device B Storage B", cursor.getString(3)); assertEquals("2", cursor.getString(4)); @@ -241,7 +242,7 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { "Device key A", false /* unopened */, new MtpRoot[0], - null, + OPERATIONS_SUPPORTED, null)); mMtpManager.addValidDevice(new MtpDeviceRecord( 1, @@ -257,7 +258,7 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { 4096 /* total space */, "Identifier B" /* no volume identifier */) }, - null, + OPERATIONS_SUPPORTED, null)); { mProvider.openDevice(0); @@ -544,14 +545,14 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { public void testBusyDevice() throws Exception { mMtpManager = new TestMtpManager(getContext()) { @Override - void openDevice(int deviceId) throws IOException { + MtpDeviceRecord openDevice(int deviceId) throws IOException { throw new BusyDeviceException(); } }; setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); mMtpManager.addValidDevice(new MtpDeviceRecord( - 0, "Device A", null /* deviceKey */, false /* unopened */, new MtpRoot[0], null, - null)); + 0, "Device A", null /* deviceKey */, false /* unopened */, new MtpRoot[0], + OPERATIONS_SUPPORTED, null)); mProvider.resumeRootScanner(); mResolver.waitForNotification(ROOTS_URI, 1); @@ -571,7 +572,8 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { public void testLockedDevice() throws Exception { setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); mMtpManager.addValidDevice(new MtpDeviceRecord( - 0, "Device A", null, false /* unopened */, new MtpRoot[0], null, null)); + 0, "Device A", null, false /* unopened */, new MtpRoot[0], OPERATIONS_SUPPORTED, + null)); mProvider.resumeRootScanner(); mResolver.waitForNotification(ROOTS_URI, 1); @@ -661,6 +663,60 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { } } + public void testCreateDocument_noWritingSupport() throws Exception { + setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); + mMtpManager.addValidDevice(new MtpDeviceRecord( + 0, "Device A", null /* deviceKey */, false /* unopened */, + new MtpRoot[] { + new MtpRoot( + 0 /* deviceId */, + 1 /* storageId */, + "Storage A" /* volume description */, + 1024 /* free space */, + 2048 /* total space */, + "" /* no volume identifier */) + }, + new int[0] /* no operations supported */, null)); + mProvider.resumeRootScanner(); + mResolver.waitForNotification(ROOTS_URI, 1); + try { + mProvider.createDocument("1", "text/palin", "note.txt"); + fail(); + } catch (UnsupportedOperationException exception) {} + } + + public void testOpenDocument_noWritingSupport() throws Exception { + setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); + mMtpManager.addValidDevice(new MtpDeviceRecord( + 0, "Device A", null /* deviceKey */, false /* unopened */, + new MtpRoot[] { + new MtpRoot( + 0 /* deviceId */, + 1 /* storageId */, + "Storage A" /* volume description */, + 1024 /* free space */, + 2048 /* total space */, + "" /* no volume identifier */) + }, + new int[0] /* no operations supported */, null)); + mMtpManager.setObjectHandles( + 0, 1, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, new int[] { 100 }); + mMtpManager.setObjectInfo( + 0, new MtpObjectInfo.Builder().setObjectHandle(100).setName("note.txt").build()); + mProvider.resumeRootScanner(); + mResolver.waitForNotification(ROOTS_URI, 1); + try (final Cursor cursor = mProvider.queryChildDocuments( + "1", strings(Document.COLUMN_DOCUMENT_ID), null)) { + assertEquals(1, cursor.getCount()); + cursor.moveToNext(); + assertEquals("3", cursor.getString(0)); + } + try { + mProvider.openDocument("3", "w", null); + fail(); + } catch (UnsupportedOperationException exception) {} + } + private void setupProvider(int flag) { mDatabase = new MtpDatabase(getContext(), flag); mProvider = new MtpDocumentsProvider(); @@ -691,7 +747,7 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { final int changeCount = mResolver.getChangeCount(ROOTS_URI); mMtpManager.addValidDevice( new MtpDeviceRecord(deviceId, "Device", null /* deviceKey */, false /* unopened */, - roots, null, null)); + roots, OPERATIONS_SUPPORTED, null)); mProvider.openDevice(deviceId); mResolver.waitForNotification(ROOTS_URI, changeCount + 1); return getStrings(mProvider.queryRoots(strings(DocumentsContract.Root.COLUMN_ROOT_ID))); diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java index 3043ce896a7e..5e0ee1e3e1fa 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java @@ -22,6 +22,7 @@ import android.os.ParcelFileDescriptor; import android.util.SparseArray; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -84,16 +85,16 @@ public class TestMtpManager extends MtpManager { } @Override - void openDevice(int deviceId) throws IOException { + MtpDeviceRecord openDevice(int deviceId) throws IOException { final MtpDeviceRecord device = mDevices.get(deviceId); if (device == null) { throw new IOException(); } - mDevices.put( - deviceId, - new MtpDeviceRecord( - device.deviceId, device.name, device.deviceKey, true, device.roots, null, - null)); + final MtpDeviceRecord record = new MtpDeviceRecord( + device.deviceId, device.name, device.deviceKey, true, device.roots, + device.operationsSupported, device.eventsSupported); + mDevices.put(deviceId, record); + return record; } @Override @@ -198,19 +199,6 @@ public class TestMtpManager extends MtpManager { } @Override - int[] getOpenedDeviceIds() { - final int[] result = new int[mDevices.size()]; - int count = 0; - for (int i = 0; i < mDevices.size(); i++) { - final MtpDeviceRecord device = mDevices.valueAt(i); - if (device.opened) { - result[count++] = device.deviceId; - } - } - return Arrays.copyOf(result, count); - } - - @Override byte[] getObject(int deviceId, int objectHandle, int expectedSize) throws IOException { return mImportFileBytes.get(pack(deviceId, objectHandle)); } diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java index 34dd77bd3747..8adb68fe676e 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java @@ -19,6 +19,7 @@ package com.android.mtp; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbManager; +import android.mtp.MtpConstants; import android.os.SystemClock; import java.io.FileNotFoundException; @@ -32,6 +33,12 @@ import java.util.Objects; final class TestUtil { private TestUtil() {} + static final int[] OPERATIONS_SUPPORTED = new int[] { + MtpConstants.OPERATION_GET_PARTIAL_OBJECT, + MtpConstants.OPERATION_SEND_OBJECT, + MtpConstants.OPERATION_SEND_OBJECT_INFO, + }; + /** * Requests permission for a MTP device and returns the first MTP device that has at least one * storage. @@ -59,14 +66,14 @@ final class TestUtil { static void addTestDevice(MtpDatabase database) throws FileNotFoundException { database.getMapper().startAddingDocuments(null); database.getMapper().putDeviceDocument(new MtpDeviceRecord( - 0, "Device", "device_key", /* opened is */ true, new MtpRoot[0], null, - null)); + 0, "Device", "device_key", /* opened is */ true, new MtpRoot[0], + OPERATIONS_SUPPORTED, null)); database.getMapper().stopAddingDocuments(null); } static void addTestStorage(MtpDatabase database, String parentId) throws FileNotFoundException { database.getMapper().startAddingDocuments(parentId); - database.getMapper().putStorageDocuments(parentId, new MtpRoot[] { + database.getMapper().putStorageDocuments(parentId, OPERATIONS_SUPPORTED, new MtpRoot[] { new MtpRoot(0, 100, "Storage", 1024, 1024, ""), }); database.getMapper().stopAddingDocuments(parentId); diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index 5b8ed28e6997..380fcd4c30ba 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -110,6 +110,7 @@ public class AccessPoint implements Comparable<AccessPoint> { private final Context mContext; private String ssid; + private String bssid; private int security; private int networkId = WifiConfiguration.INVALID_NETWORK_ID; @@ -335,6 +336,10 @@ public class AccessPoint implements Comparable<AccessPoint> { return ssid; } + public String getBssid() { + return bssid; + } + public CharSequence getSsid() { SpannableString str = new SpannableString(ssid); str.setSpan(new TtsSpan.VerbatimBuilder(ssid).build(), 0, ssid.length(), @@ -657,6 +662,7 @@ public class AccessPoint implements Comparable<AccessPoint> { else ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID)); + bssid = config.BSSID; security = getSecurity(config); networkId = config.networkId; mConfig = config; @@ -664,6 +670,7 @@ public class AccessPoint implements Comparable<AccessPoint> { private void initWithScanResult(ScanResult result) { ssid = result.SSID; + bssid = result.BSSID; security = getSecurity(result); if (security == SECURITY_PSK) pskType = getPskType(result); diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index 587fd6cb7a83..36097a124ff9 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -436,10 +436,12 @@ public class BugreportProgressService extends Service { final Intent infoIntent = new Intent(mContext, BugreportProgressService.class); infoIntent.setAction(INTENT_BUGREPORT_INFO_LAUNCH); infoIntent.putExtra(EXTRA_ID, info.id); + final PendingIntent infoPendingIntent = + PendingIntent.getService(mContext, info.id, infoIntent, + PendingIntent.FLAG_UPDATE_CURRENT); final Action infoAction = new Action.Builder(null, mContext.getString(R.string.bugreport_info_action), - PendingIntent.getService(mContext, info.id, infoIntent, - PendingIntent.FLAG_UPDATE_CURRENT)).build(); + infoPendingIntent).build(); final Intent screenshotIntent = new Intent(mContext, BugreportProgressService.class); screenshotIntent.setAction(INTENT_BUGREPORT_SCREENSHOT); screenshotIntent.putExtra(EXTRA_ID, info.id); @@ -466,6 +468,7 @@ public class BugreportProgressService extends Service { .setLocalOnly(true) .setColor(mContext.getColor( com.android.internal.R.color.system_notification_accent_color)) + .setContentIntent(infoPendingIntent) .addAction(infoAction) .addAction(screenshotAction) .addAction(cancelAction) diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java index 3cef3bb21a7f..d0499a5e3946 100644 --- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java +++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java @@ -198,6 +198,25 @@ public class BugreportReceiverTest extends InstrumentationTestCase { assertServiceNotRunning(); } + public void testProgress_cancel() throws Exception { + resetProperties(); + sendBugreportStarted(1000); + waitForScreenshotButtonEnabled(true); + + final NumberFormat nf = NumberFormat.getPercentInstance(); + nf.setMinimumFractionDigits(2); + nf.setMaximumFractionDigits(2); + + assertProgressNotification(NAME, nf.format(0)); + + openProgressNotification(ID); + UiObject cancelButton = mUiBot.getVisibleObject(mContext.getString( + com.android.internal.R.string.cancel).toUpperCase()); + mUiBot.click(cancelButton, "cancel_button"); + + waitForService(false); + } + public void testProgress_takeExtraScreenshot() throws Exception { takeExtraScreenshotTest(false); } @@ -339,12 +358,20 @@ public class BugreportReceiverTest extends InstrumentationTestCase { assertServiceNotRunning(); } - public void testProgress_changeJustDetails() throws Exception { + public void testProgress_changeJustDetailsTouchingDetails() throws Exception { + changeJustDetailsTest(true); + } + + public void testProgress_changeJustDetailsTouchingNotification() throws Exception { + changeJustDetailsTest(false); + } + + private void changeJustDetailsTest(boolean touchDetails) throws Exception { resetProperties(); sendBugreportStarted(1000); waitForScreenshotButtonEnabled(true); - DetailsUi detailsUi = new DetailsUi(mUiBot, ID); + DetailsUi detailsUi = new DetailsUi(mUiBot, ID, touchDetails); detailsUi.nameField.setText(""); detailsUi.titleField.setText(""); @@ -527,10 +554,10 @@ public class BugreportReceiverTest extends InstrumentationTestCase { mUiBot.getObject(percent); } - private void openProgressNotification(int id) { + private UiObject openProgressNotification(int id) { String title = mContext.getString(R.string.bugreport_in_progress_title, id); Log.v(TAG, "Looking for progress notification title: '" + title + "'"); - mUiBot.getNotification(title); + return mUiBot.getNotification(title); } void resetProperties() { @@ -899,11 +926,23 @@ public class BugreportReceiverTest extends InstrumentationTestCase { * Gets the UI objects by opening the progress notification and clicking DETAILS. */ DetailsUi(UiBot uiBot, int id) throws UiObjectNotFoundException { - openProgressNotification(id); + this(uiBot, id, true); + } + + /** + * Gets the UI objects by opening the progress notification and clicking on DETAILS or in + * the notification itself. + */ + DetailsUi(UiBot uiBot, int id, boolean clickDetails) throws UiObjectNotFoundException { + UiObject notification = openProgressNotification(id); detailsButton = mUiBot.getVisibleObject(mContext.getString( R.string.bugreport_info_action).toUpperCase()); - mUiBot.click(detailsButton, "details_button"); + if (clickDetails) { + mUiBot.click(detailsButton, "details_button"); + } else { + mUiBot.click(notification, "notification"); + } // TODO: unhardcode resource ids UiObject dialogTitle = mUiBot.getVisibleObjectById("android:id/alertTitle"); assertEquals("Wrong title", mContext.getString(R.string.bugreport_info_dialog_title, diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml index 4d0eb96f4eae..62f2b479cde9 100644 --- a/packages/SystemUI/res/layout/notification_guts.xml +++ b/packages/SystemUI/res/layout/notification_guts.xml @@ -26,7 +26,7 @@ android:orientation="vertical" android:paddingStart="@*android:dimen/notification_content_margin_start" android:paddingEnd="8dp" - android:background="@color/notification_guts_bg_color" > + android:background="@color/notification_guts_bg_color"> <!-- header --> <LinearLayout @@ -58,8 +58,38 @@ android:layout_gravity="bottom|start" android:visibility="gone" /> </LinearLayout> + <!-- Importance radio buttons --> + <RadioGroup + android:id="@+id/importance_buttons" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="8dp" + android:paddingBottom="8dip" + android:paddingEnd="8dp" > + <RadioButton + android:id="@+id/silent_importance" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:text="@string/show_silently" + style="@style/TextAppearance.NotificationGuts.Primary" + android:buttonTint="#858383" /> + <RadioButton + android:id="@+id/block_importance" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:text="@string/block" + style="@style/TextAppearance.NotificationGuts.Primary" + android:buttonTint="#858383" /> + <RadioButton + android:id="@+id/reset_importance" + android:layout_width="wrap_content" + android:layout_height="48dp" + style="@style/TextAppearance.NotificationGuts.Primary" + android:buttonTint="#858383" /> + </RadioGroup> <!-- Importance slider --> <LinearLayout + android:id="@+id/importance_slider" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" @@ -67,7 +97,8 @@ android:clickable="false" android:focusable="false" android:paddingBottom="8dip" - android:paddingEnd="8dp" > + android:paddingEnd="8dp" + android:visibility="gone"> <TextView android:id="@+id/title" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml index 458ce2decaff..582ad48c189e 100644 --- a/packages/SystemUI/res/layout/qs_customize_panel.xml +++ b/packages/SystemUI/res/layout/qs_customize_panel.xml @@ -34,7 +34,9 @@ android:id="@android:id/list" android:layout_width="@dimen/notification_panel_width" android:layout_height="0dp" - android:layout_weight="1" /> + android:layout_weight="1" + android:scrollIndicators="top" + android:scrollbars="vertical" /> <View android:layout_width="match_parent" diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index ac6e3ac7aae4..1f239c3d1aa8 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -497,6 +497,8 @@ <string name="accessibility_quick_settings_less_time">Less time.</string> <!-- Content description of the flashlight tile in quick settings when off (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_quick_settings_flashlight_off">Flashlight off.</string> + <!-- Content description of the flashlight tile in quick settings when unavailable (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_quick_settings_flashlight_unavailable">Flashlight unavailable.</string> <!-- Content description of the flashlight tile in quick settings when on (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_quick_settings_flashlight_on">Flashlight on.</string> <!-- Announcement made when the flashlight state changes to off (not shown on the screen). [CHAR LIMIT=NONE] --> @@ -1205,12 +1207,22 @@ <!-- Bluetooth enablement ok text [CHAR LIMIT=40] --> <string name="enable_bluetooth_confirmation_ok">Turn on</string> - <!-- Apply notification importance setting to a topic [CHAR LIMIT=NONE] --> - <string name="apply_to_topic">Apply to <xliff:g id="topic_name" example="Friend Request">%1$s</xliff:g> notifications</string> - <!-- Apply notification importance setting to an app [CHAR LIMIT=NONE] --> - <string name="apply_to_app">Apply to all notifications from this app</string> + <!-- [CHAR LIMIT=100] Notification importance option --> + <string name="show_silently">Show notifications silently</string> + <!-- [CHAR LIMIT=100] Notification importance option --> + <string name="block">Block all notifications</string> + <!-- [CHAR LIMIT=100] Notification importance option --> + <string name="do_not_silence">Don\'t silence</string> + <!-- [CHAR LIMIT=100] Notification importance option --> + <string name="do_not_silence_block">Don\'t silence or block</string> + + <!-- [CHAR LIMIT=NONE] Importance Tuner setting title --> + <string name="tuner_full_importance_settings">Show full importance settings</string> + <!-- Notification importance title, blocked status--> <string name="blocked_importance">Blocked</string> + <!-- Notification importance title, min status--> + <string name="min_importance">Min importance</string> <!-- Notification importance title, low status--> <string name="low_importance">Low importance</string> <!-- Notification importance title, normal status--> @@ -1223,17 +1235,20 @@ <!-- [CHAR LIMIT=100] Notification Importance slider: blocked importance level description --> <string name="notification_importance_blocked">Never show these notifications</string> + <!-- [CHAR LIMIT=100] Notification Importance slider: min importance level description --> + <string name="notification_importance_min">Silently show at the bottom of the notification list</string> + <!-- [CHAR LIMIT=100] Notification Importance slider: low importance level description --> - <string name="notification_importance_low">Silently show at the bottom of the notification list</string> + <string name="notification_importance_low">Silently show these notifications</string> <!-- [CHAR LIMIT=100] Notification Importance slider: normal importance level description --> - <string name="notification_importance_default">Silently show these notifications</string> + <string name="notification_importance_default">Allow these notification to make sounds</string> <!-- [CHAR LIMIT=100] Notification Importance slider: high importance level description --> - <string name="notification_importance_high">Show at the top of the notifications list and make sound</string> + <string name="notification_importance_high">Peek onto the screen and allow sound and allow sound</string> <!-- [CHAR LIMIT=100] Notification Importance slider: max importance level description --> - <string name="notification_importance_max">Peek onto the screen and make sound</string> + <string name="notification_importance_max">Show at the top of the notifications list, peek onto the screen and allow sound</string> <!-- Notification: Control panel: Label for button that launches notification settings. [CHAR LIMIT=NONE] --> <string name="notification_more_settings">More settings</string> @@ -1434,6 +1449,9 @@ <item>Don\'t show this icon</item> </string-array> + <!-- SysUI Tuner: Other section --> + <string name="other">Other</string> + <!-- Accessibility label for the divider that separates the windows in split-screen mode [CHAR LIMIT=NONE] --> <string name="accessibility_divider">Split-screen divider</string> @@ -1448,4 +1466,5 @@ <!-- Accessibility action for moving down the docked stack divider [CHAR LIMIT=NONE] --> <string name="accessibility_action_divider_move_right">Move right</string> + </resources> diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml index f4a0cc92f30d..ddc03a39d3a7 100644 --- a/packages/SystemUI/res/xml/tuner_prefs.xml +++ b/packages/SystemUI/res/xml/tuner_prefs.xml @@ -134,8 +134,8 @@ --> <PreferenceScreen - android:key="overview" - android:title="@string/overview" > + android:key="other" + android:title="@string/other" > <com.android.systemui.tuner.TunerSwitch android:key="overview_disable_fast_toggle_via_button" @@ -147,6 +147,11 @@ android:title="@string/overview_nav_bar_gesture" android:summary="@string/overview_nav_bar_gesture_desc" /> + <!-- importance --> + <com.android.systemui.tuner.TunerSwitch + android:key="show_importance_slider" + android:title="@string/tuner_full_importance_settings" /> + </PreferenceScreen> <!-- Warning, this goes last. --> diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java index 147162232155..9f2745b9ac24 100644 --- a/packages/SystemUI/src/com/android/systemui/Prefs.java +++ b/packages/SystemUI/src/com/android/systemui/Prefs.java @@ -48,6 +48,7 @@ public final class Prefs { Key.QS_DATA_SAVER_ADDED, Key.QS_INVERT_COLORS_ADDED, Key.QS_WORK_ADDED, + Key.QS_NIGHT_ADDED, }) public @interface Key { String OVERVIEW_SEARCH_APP_WIDGET_ID = "searchAppWidgetId"; @@ -68,6 +69,7 @@ public final class Prefs { String QS_DATA_SAVER_ADDED = "QsDataSaverAdded"; String QS_INVERT_COLORS_ADDED = "QsInvertColorsAdded"; String QS_WORK_ADDED = "QsWorkAdded"; + String QS_NIGHT_ADDED = "QsNightAdded"; } public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java index 876f41798521..c6c497ffde75 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java @@ -107,7 +107,10 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta mOtherTiles = new ArrayList<TileInfo>(mAllTiles); mTiles.clear(); for (int i = 0; i < mCurrentSpecs.size(); i++) { - mTiles.add(getAndRemoveOther(mCurrentSpecs.get(i))); + final TileInfo tile = getAndRemoveOther(mCurrentSpecs.get(i)); + if (tile != null) { + mTiles.add(tile); + } } mTiles.add(null); mTiles.addAll(mOtherTiles); diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java index c3610d93e193..aa85f784fe4b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java @@ -28,11 +28,14 @@ import android.os.AsyncTask; import android.os.Handler; import android.os.Looper; import android.service.quicksettings.TileService; +import com.android.systemui.Prefs; +import com.android.systemui.Prefs.Key; import com.android.systemui.R; import com.android.systemui.qs.QSTile; import com.android.systemui.qs.QSTile.DrawableIcon; import com.android.systemui.qs.external.CustomTile; import com.android.systemui.statusbar.phone.QSTileHost; +import com.android.systemui.tuner.TunerService; import java.util.ArrayList; import java.util.Collection; @@ -54,7 +57,8 @@ public class TileQueryHelper { } private void addSystemTiles(QSTileHost host) { - boolean hasColorMod = host.getNightModeController().isEnabled(); + boolean hasColorMod = Prefs.getBoolean(host.getContext(), Key.QS_NIGHT_ADDED, false) + && TunerService.isTunerEnabled(host.getContext()); String possible = mContext.getString(R.string.quick_settings_tiles_default) + ",hotspot,inversion,saver,work,cast" + (hasColorMod ? ",night" : ""); String[] possibleTiles = possible.split(","); @@ -88,7 +92,12 @@ public class TileQueryHelper { qsHandler.post(new Runnable() { @Override public void run() { - new QueryTilesTask().execute(); + mainHandler.post(new Runnable() { + @Override + public void run() { + new QueryTilesTask().execute(); + } + }); } }); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java index c10843a30547..e4e379058399 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java @@ -17,9 +17,11 @@ package com.android.systemui.qs.tiles; import android.app.ActivityManager; - import android.content.Intent; +import android.graphics.drawable.Drawable; import android.provider.MediaStore; +import android.text.SpannableStringBuilder; +import android.text.style.ForegroundColorSpan; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsProto.MetricsEvent; import com.android.systemui.R; @@ -79,9 +81,19 @@ public class FlashlightTile extends QSTile<QSTile.BooleanState> implements @Override protected void handleUpdateState(BooleanState state, Object arg) { - // TODO: Flashlight available handling... -// state.visible = mFlashlightController.isAvailable(); state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label); + if (!mFlashlightController.isAvailable()) { + Drawable icon = mHost.getContext().getDrawable(R.drawable.ic_signal_flashlight_disable); + final int disabledColor = mHost.getContext().getColor(R.color.qs_tile_tint_unavailable); + icon.setTint(disabledColor); + state.icon = new DrawableIcon(icon); + state.label = new SpannableStringBuilder().append(state.label, + new ForegroundColorSpan(disabledColor), + SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE); + state.contentDescription = mContext.getString( + R.string.accessibility_quick_settings_flashlight_unavailable); + return; + } if (arg instanceof Boolean) { boolean value = (Boolean) arg; if (value == state.value) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 6be95124ffa6..9bd645d79240 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -110,7 +110,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; -import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_MAX; +import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH; import static com.android.keyguard.KeyguardHostView.OnDismissAction; public abstract class BaseStatusBar extends SystemUI implements @@ -1007,7 +1007,7 @@ public abstract class BaseStatusBar extends SystemUI implements } }); - guts.bindImportance(sbn, row, mNotificationData.getImportance(sbn.getKey())); + guts.bindImportance(pmUser, sbn, row, mNotificationData.getImportance(sbn.getKey())); } protected GearDisplayedListener getGearDisplayedListener() { @@ -1044,9 +1044,9 @@ public abstract class BaseStatusBar extends SystemUI implements MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_CONTROLS); - // ensure that it's layouted but not visible until actually laid out + // ensure that it's laid but not visible until actually laid out guts.setVisibility(View.INVISIBLE); - // Post to ensure the the guts are properly layed out. + // Post to ensure the the guts are properly laid out. guts.post(new Runnable() { public void run() { dismissPopups(); @@ -2215,25 +2215,25 @@ public abstract class BaseStatusBar extends SystemUI implements return false; } - if (sbn.getNotification().fullScreenIntent != null) { - if (mAccessibilityManager.isTouchExplorationEnabled()) { - if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey()); - return false; - } else { - return true; - } - } - if (isSnoozedPackage(sbn)) { if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey()); return false; } - if (mNotificationData.getImportance(sbn.getKey()) < IMPORTANCE_MAX) { + if (mNotificationData.getImportance(sbn.getKey()) < IMPORTANCE_HIGH) { if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey()); return false; } + if (sbn.getNotification().fullScreenIntent != null) { + if (mAccessibilityManager.isTouchExplorationEnabled()) { + if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey()); + return false; + } else { + return true; + } + } + return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java index ccd0ad857bbe..e300614da268 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java @@ -203,11 +203,11 @@ public class NotificationData { String mediaNotification = mEnvironment.getCurrentMediaNotificationKey(); - // PRIORITY_MIN media streams are allowed to drift to the bottom + // IMPORTANCE_MIN media streams are allowed to drift to the bottom final boolean aMedia = a.key.equals(mediaNotification) - && aImportance > Ranking.IMPORTANCE_LOW; + && aImportance > Ranking.IMPORTANCE_MIN; final boolean bMedia = b.key.equals(mediaNotification) - && bImportance > Ranking.IMPORTANCE_LOW; + && bImportance > Ranking.IMPORTANCE_MIN; boolean aSystemMax = aImportance >= Ranking.IMPORTANCE_MAX && isSystemNotification(na); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java index fe84d81354dd..1c16bdc0d7bb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java @@ -17,7 +17,6 @@ package com.android.systemui.statusbar; import android.app.INotificationManager; -import android.app.Notification; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -39,23 +38,31 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsProto.MetricsEvent; import com.android.settingslib.Utils; import com.android.systemui.R; +import com.android.systemui.tuner.TunerService; /** * The guts of a notification revealed when performing a long press. */ -public class NotificationGuts extends LinearLayout { +public class NotificationGuts extends LinearLayout implements TunerService.Tunable { + public static final String SHOW_SLIDER = "show_importance_slider"; private Drawable mBackground; private int mClipTopAmount; private int mActualHeight; private boolean mExposed; - private SeekBar mSeekBar; private INotificationManager mINotificationManager; private int mStartingImportance; + private boolean mShowSlider; + + private SeekBar mSeekBar; + private RadioButton mBlock; + private RadioButton mSilent; + private RadioButton mReset; public NotificationGuts(Context context, AttributeSet attrs) { super(context, attrs); setWillNotDraw(false); + TunerService.get(mContext).addTunable(this, SHOW_SLIDER); } @Override @@ -102,31 +109,81 @@ public class NotificationGuts extends LinearLayout { } } - void bindImportance(final StatusBarNotification sbn, final ExpandableNotificationRow row, - final int importance) { + void bindImportance(final PackageManager pm, final StatusBarNotification sbn, + final ExpandableNotificationRow row, final int importance) { mStartingImportance = importance; mINotificationManager = INotificationManager.Stub.asInterface( ServiceManager.getService(Context.NOTIFICATION_SERVICE)); - - final TextView importanceSummary = ((TextView) row.findViewById(R.id.summary)); - final TextView importanceTitle = ((TextView) row.findViewById(R.id.title)); - mSeekBar = (SeekBar) row.findViewById(R.id.seekbar); boolean systemApp = false; try { - final PackageManager pm = BaseStatusBar.getPackageManagerForUser( - getContext(), sbn.getUser().getIdentifier()); final PackageInfo info = pm.getPackageInfo(sbn.getPackageName(), PackageManager.GET_SIGNATURES); systemApp = Utils.isSystemPackage(pm, info); } catch (PackageManager.NameNotFoundException e) { // unlikely. } + + final View importanceSlider = row.findViewById(R.id.importance_slider); + final View importanceButtons = row.findViewById(R.id.importance_buttons); + if (mShowSlider) { + bindSlider(importanceSlider, sbn, systemApp); + importanceSlider.setVisibility(View.VISIBLE); + importanceButtons.setVisibility(View.GONE); + } else { + bindToggles(importanceButtons, sbn, systemApp); + importanceButtons.setVisibility(View.VISIBLE); + importanceSlider.setVisibility(View.GONE); + } + } + + void saveImportance(final StatusBarNotification sbn) { + int progress; + if (mSeekBar!= null && mSeekBar.isShown()) { + progress = mSeekBar.getProgress(); + } else { + if (mBlock.isChecked()) { + progress = NotificationListenerService.Ranking.IMPORTANCE_NONE; + } else if (mSilent.isChecked()) { + progress = NotificationListenerService.Ranking.IMPORTANCE_DEFAULT; + } else { + progress = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED; + } + } + MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE, + progress - mStartingImportance); + try { + mINotificationManager.setImportance(sbn.getPackageName(), sbn.getUid(), progress); + } catch (RemoteException e) { + // :( + } + } + + private void bindToggles(final View importanceButtons, final StatusBarNotification sbn, + final boolean systemApp) { + mBlock = (RadioButton) importanceButtons.findViewById(R.id.block_importance); + mSilent = (RadioButton) importanceButtons.findViewById(R.id.silent_importance); + mReset = (RadioButton) importanceButtons.findViewById(R.id.reset_importance); if (systemApp) { - ((ImageView) row.findViewById(R.id.low_importance)).getDrawable().setTint( + mBlock.setVisibility(View.GONE); + mReset.setText(mContext.getString(R.string.do_not_silence)); + } else { + mReset.setText(mContext.getString(R.string.do_not_silence_block)); + } + mReset.setChecked(true); + } + + private void bindSlider(final View importanceSlider, final StatusBarNotification sbn, + final boolean systemApp) { + final TextView importanceSummary = ((TextView) importanceSlider.findViewById(R.id.summary)); + final TextView importanceTitle = ((TextView) importanceSlider.findViewById(R.id.title)); + mSeekBar = (SeekBar) importanceSlider.findViewById(R.id.seekbar); + + if (systemApp) { + ((ImageView) importanceSlider.findViewById(R.id.low_importance)).getDrawable().setTint( mContext.getColor(R.color.notification_guts_disabled_icon_tint)); } final int minProgress = systemApp ? - NotificationListenerService.Ranking.IMPORTANCE_LOW + NotificationListenerService.Ranking.IMPORTANCE_MIN : NotificationListenerService.Ranking.IMPORTANCE_NONE; mSeekBar.setMax(NotificationListenerService.Ranking.IMPORTANCE_MAX); mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @@ -159,6 +216,11 @@ public class NotificationGuts extends LinearLayout { R.string.notification_importance_blocked)); importanceTitle.setText(mContext.getString(R.string.blocked_importance)); break; + case NotificationListenerService.Ranking.IMPORTANCE_MIN: + importanceSummary.setText(mContext.getString( + R.string.notification_importance_min)); + importanceTitle.setText(mContext.getString(R.string.min_importance)); + break; case NotificationListenerService.Ranking.IMPORTANCE_LOW: importanceSummary.setText(mContext.getString( R.string.notification_importance_low)); @@ -182,18 +244,7 @@ public class NotificationGuts extends LinearLayout { } } }); - mSeekBar.setProgress(importance); - } - - void saveImportance(final StatusBarNotification sbn) { - int progress = mSeekBar.getProgress(); - MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE, - progress - mStartingImportance); - try { - mINotificationManager.setImportance(sbn.getPackageName(), sbn.getUid(), progress); - } catch (RemoteException e) { - // :( - } + mSeekBar.setProgress(mStartingImportance); } public void setActualHeight(int actualHeight) { @@ -224,4 +275,11 @@ public class NotificationGuts extends LinearLayout { public boolean areGutsExposed() { return mExposed; } + + @Override + public void onTuningChanged(String key, String newValue) { + if (SHOW_SLIDER.equals(key)) { + mShowSlider = newValue != null && Integer.parseInt(newValue) != 0; + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java index b74247960fc7..6d0fbb15ca64 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java @@ -24,6 +24,7 @@ import com.android.systemui.statusbar.policy.DataSaverController; import com.android.systemui.statusbar.policy.DataSaverController.Listener; import com.android.systemui.statusbar.policy.HotspotController; import com.android.systemui.statusbar.policy.HotspotController.Callback; +import com.android.systemui.statusbar.policy.NightModeController; /** * Manages which tiles should be automatically added to QS. @@ -66,12 +67,33 @@ public class AutoTileManager { if (!Prefs.getBoolean(context, Key.QS_WORK_ADDED, false)) { host.getManagedProfileController().addCallback(mProfileCallback); } + if (!Prefs.getBoolean(context, Key.QS_NIGHT_ADDED, false)) { + host.getNightModeController().addListener(mNightModeListener); + } } public void destroy() { // TODO: Remove any registered listeners. } + private final NightModeController.Listener mNightModeListener = + new NightModeController.Listener() { + @Override + public void onNightModeChanged() { + mHost.addTile("night"); + Prefs.putBoolean(mContext, Key.QS_NIGHT_ADDED, true); + mHandler.post(new Runnable() { + @Override + public void run() { + mHost.getNightModeController().removeListener(mNightModeListener); + } + }); + } + + @Override + public void onTwilightAutoChanged() { } + }; + private final ManagedProfileController.Callback mProfileCallback = new ManagedProfileController.Callback() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index ac714e737210..9b87a8adbeb6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -1319,6 +1319,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (DEBUG) { Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + notification.getKey()); } + } else if (mNotificationData.getImportance(notification.getKey()) + < NotificationListenerService.Ranking.IMPORTANCE_MAX) { + if (DEBUG) { + Log.d(TAG, "No Fullscreen intent: not important enough: " + + notification.getKey()); + } } else { // Stop screensaver if the notification has a full-screen intent. // (like an incoming phone call) diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java index 4171dbcc4372..a392becbcf98 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java @@ -93,6 +93,17 @@ public class PipMenuActivity extends Activity implements PipManager.Listener { }); } + private void restorePipAndFinish() { + mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY); + finish(); + } + + @Override + public void onPause() { + super.onPause(); + restorePipAndFinish(); + } + @Override protected void onDestroy() { super.onDestroy(); @@ -103,8 +114,7 @@ public class PipMenuActivity extends Activity implements PipManager.Listener { @Override public void onBackPressed() { - mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY); - finish(); + restorePipAndFinish(); } @Override diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index e32d89c0d36a..f1a9c44ffe6c 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -171,6 +171,10 @@ public class BackupManagerService { static final boolean MORE_DEBUG = false; static final boolean DEBUG_SCHEDULING = MORE_DEBUG || true; + // File containing backup-enabled state. Contains a single byte; + // nonzero == enabled. File missing or contains a zero byte == disabled. + static final String BACKUP_ENABLE_FILE = "backup_enabled"; + // System-private key used for backing up an app's widget state. Must // begin with U+FFxx by convention (we reserve all keys starting // with U+FF00 or higher for system use). @@ -354,11 +358,30 @@ public class BackupManagerService { if (userId == UserHandle.USER_SYSTEM) { sInstance.initialize(userId); - ContentResolver r = sInstance.mContext.getContentResolver(); - boolean areEnabled = Settings.Secure.getIntForUser(r, - Settings.Secure.BACKUP_ENABLED, 0, userId) != 0; + // Migrate legacy setting + if (!backupSettingMigrated(userId)) { + if (DEBUG) { + Slog.i(TAG, "Backup enable apparently not migrated"); + } + final ContentResolver r = sInstance.mContext.getContentResolver(); + final int enableState = Settings.Secure.getIntForUser(r, + Settings.Secure.BACKUP_ENABLED, -1, userId); + if (enableState >= 0) { + if (DEBUG) { + Slog.i(TAG, "Migrating enable state " + (enableState != 0)); + } + writeBackupEnableState(enableState != 0, userId); + Settings.Secure.putStringForUser(r, + Settings.Secure.BACKUP_ENABLED, null, userId); + } else { + if (DEBUG) { + Slog.i(TAG, "Backup not yet configured; retaining null enable state"); + } + } + } + try { - sInstance.setBackupEnabled(areEnabled); + sInstance.setBackupEnabled(readBackupEnableState(userId)); } catch (RemoteException e) { // can't happen; it's a local object } @@ -9314,6 +9337,58 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF } } + private static boolean backupSettingMigrated(int userId) { + File base = new File(Environment.getDataDirectory(), "backup"); + File enableFile = new File(base, BACKUP_ENABLE_FILE); + return enableFile.exists(); + } + + private static boolean readBackupEnableState(int userId) { + File base = new File(Environment.getDataDirectory(), "backup"); + File enableFile = new File(base, BACKUP_ENABLE_FILE); + if (enableFile.exists()) { + try (FileInputStream fin = new FileInputStream(enableFile)) { + int state = fin.read(); + return state != 0; + } catch (IOException e) { + // can't read the file; fall through to assume disabled + Slog.e(TAG, "Cannot read enable state; assuming disabled"); + } + } else { + if (DEBUG) { + Slog.i(TAG, "isBackupEnabled() => false due to absent settings file"); + } + } + return false; + } + + private static void writeBackupEnableState(boolean enable, int userId) { + File base = new File(Environment.getDataDirectory(), "backup"); + File enableFile = new File(base, BACKUP_ENABLE_FILE); + File stage = new File(base, BACKUP_ENABLE_FILE + "-stage"); + FileOutputStream fout = null; + try { + fout = new FileOutputStream(stage); + fout.write(enable ? 1 : 0); + fout.close(); + stage.renameTo(enableFile); + // will be synced immediately by the try-with-resources call to close() + } catch (IOException|RuntimeException e) { + // Whoops; looks like we're doomed. Roll everything out, disabled, + // including the legacy state. + Slog.e(TAG, "Unable to record backup enable state; reverting to disabled: " + + e.getMessage()); + + final ContentResolver r = sInstance.mContext.getContentResolver(); + Settings.Secure.putStringForUser(r, + Settings.Secure.BACKUP_ENABLED, null, userId); + enableFile.delete(); + stage.delete(); + } finally { + IoUtils.closeQuietly(fout); + } + } + // Enable/disable backups public void setBackupEnabled(boolean enable) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, @@ -9325,8 +9400,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF try { boolean wasEnabled = mEnabled; synchronized (this) { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0); + writeBackupEnableState(enable, UserHandle.USER_SYSTEM); mEnabled = enable; } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index e14b837f3373..b594ae80c8a6 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -13393,6 +13393,14 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override + public int getMemoryTrimLevel() { + enforceNotIsolatedCaller("getMyMemoryState"); + synchronized (this) { + return mLastMemoryLevel; + } + } + + @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ResultReceiver resultReceiver) { (new ActivityManagerShellCommand(this, false)).exec( diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index 4bf77d3e150f..bd888d809d78 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -53,9 +53,11 @@ import android.os.UserHandle; import android.util.Slog; import android.util.SparseArray; -import android.util.SparseBooleanArray; +import android.util.SparseIntArray; +import android.util.TimeUtils; import com.android.internal.app.IBatteryStats; import com.android.internal.util.ArrayUtils; +import com.android.internal.app.ProcessStats; import com.android.server.DeviceIdleController; import com.android.server.LocalServices; import com.android.server.job.JobStore.JobStatusFunctor; @@ -87,9 +89,8 @@ public final class JobSchedulerService extends com.android.server.SystemService static final String TAG = "JobSchedulerService"; public static final boolean DEBUG = false; - /** The number of concurrent jobs we run at one time. */ - private static final int MAX_JOB_CONTEXTS_COUNT - = ActivityManager.isLowRamDeviceStatic() ? 3 : 6; + /** The maximum number of concurrent jobs we run at one time. */ + private static final int MAX_JOB_CONTEXTS_COUNT = 8; /** Enforce a per-app limit on scheduled jobs? */ private static final boolean ENFORCE_MAX_JOBS = false; /** The maximum number of jobs that we allow an unprivileged app to schedule */ @@ -171,9 +172,33 @@ public final class JobSchedulerService extends com.android.server.SystemService boolean mReportedActive; /** + * Current limit on the number of concurrent JobServiceContext entries we want to + * keep actively running a job. + */ + int mMaxActiveJobs = MAX_JOB_CONTEXTS_COUNT - 2; + + /** * Which uids are currently in the foreground. */ - final SparseBooleanArray mForegroundUids = new SparseBooleanArray(); + final SparseIntArray mUidPriorityOverride = new SparseIntArray(); + + // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked -- + + /** + * This array essentially stores the state of mActiveServices array. + * The ith index stores the job present on the ith JobServiceContext. + * We manipulate this array until we arrive at what jobs should be running on + * what JobServiceContext. + */ + JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT]; + /** + * Indicates whether we need to act on this jobContext id + */ + boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT]; + /** + * The uid whose jobs we would like to assign to a context. + */ + int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT]; /** * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we @@ -376,19 +401,15 @@ public final class JobSchedulerService extends com.android.server.SystemService void updateUidState(int uid, int procState) { synchronized (mLock) { - boolean foreground = procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; - boolean changed = false; - if (foreground) { - if (!mForegroundUids.get(uid)) { - changed = true; - mForegroundUids.put(uid, true); - } + if (procState == ActivityManager.PROCESS_STATE_TOP) { + // Only use this if we are exactly the top app. All others can live + // with just the foreground priority. This means that persistent processes + // can never be the top app priority... that is fine. + mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP); + } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { + mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP); } else { - int index = mForegroundUids.indexOfKey(uid); - if (index >= 0) { - mForegroundUids.removeAt(index); - changed = true; - } + mUidPriorityOverride.delete(uid); } } } @@ -1007,8 +1028,9 @@ public final class JobSchedulerService extends com.android.server.SystemService if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) { return priority; } - if (mForegroundUids.get(job.getSourceUid())) { - return JobInfo.PRIORITY_FOREGROUND_APP; + int override = mUidPriorityOverride.get(job.getSourceUid(), 0); + if (override != 0) { + return override; } return priority; } @@ -1024,24 +1046,44 @@ public final class JobSchedulerService extends com.android.server.SystemService Slog.d(TAG, printPendingQueue()); } - // This array essentially stores the state of mActiveServices array. - // ith index stores the job present on the ith JobServiceContext. - // We manipulate this array until we arrive at what jobs should be running on - // what JobServiceContext. - JobStatus[] contextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT]; - // Indicates whether we need to act on this jobContext id - boolean[] act = new boolean[MAX_JOB_CONTEXTS_COUNT]; - int[] preferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT]; - for (int i=0; i<mActiveServices.size(); i++) { - contextIdToJobMap[i] = mActiveServices.get(i).getRunningJob(); - preferredUidForContext[i] = mActiveServices.get(i).getPreferredUid(); + int memLevel; + try { + memLevel = ActivityManagerNative.getDefault().getMemoryTrimLevel(); + } catch (RemoteException e) { + memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL; + } + switch (memLevel) { + case ProcessStats.ADJ_MEM_FACTOR_MODERATE: + mMaxActiveJobs = ((MAX_JOB_CONTEXTS_COUNT - 2) * 2) / 3; + break; + case ProcessStats.ADJ_MEM_FACTOR_LOW: + mMaxActiveJobs = (MAX_JOB_CONTEXTS_COUNT - 2) / 3; + break; + case ProcessStats.ADJ_MEM_FACTOR_CRITICAL: + mMaxActiveJobs = 1; + break; + default: + mMaxActiveJobs = MAX_JOB_CONTEXTS_COUNT - 2; + break; + } + + JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap; + boolean[] act = mTmpAssignAct; + int[] preferredUidForContext = mTmpAssignPreferredUidForContext; + int numActive = 0; + for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { + final JobServiceContext js = mActiveServices.get(i); + if ((contextIdToJobMap[i] = js.getRunningJob()) != null) { + numActive++; + } + act[i] = false; + preferredUidForContext[i] = js.getPreferredUid(); } if (DEBUG) { Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial")); } - Iterator<JobStatus> it = mPendingJobs.iterator(); - while (it.hasNext()) { - JobStatus nextPending = it.next(); + for (int i=0; i<mPendingJobs.size(); i++) { + JobStatus nextPending = mPendingJobs.get(i); // If job is already running, go to next job. int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap); @@ -1049,24 +1091,30 @@ public final class JobSchedulerService extends com.android.server.SystemService continue; } - nextPending.lastEvaluatedPriority = evaluateJobPriorityLocked(nextPending); + final int priority = evaluateJobPriorityLocked(nextPending); + nextPending.lastEvaluatedPriority = priority; // Find a context for nextPending. The context should be available OR // it should have lowest priority among all running jobs // (sharing the same Uid as nextPending) int minPriority = Integer.MAX_VALUE; int minPriorityContextId = -1; - for (int i=0; i<mActiveServices.size(); i++) { - JobStatus job = contextIdToJobMap[i]; - int preferredUid = preferredUidForContext[i]; - if (job == null && (preferredUid == nextPending.getUid() || - preferredUid == JobServiceContext.NO_PREFERRED_UID) ) { - minPriorityContextId = i; - break; - } + for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) { + JobStatus job = contextIdToJobMap[j]; + int preferredUid = preferredUidForContext[j]; if (job == null) { + if ((numActive < mMaxActiveJobs || priority >= JobInfo.PRIORITY_TOP_APP) && + (preferredUid == nextPending.getUid() || + preferredUid == JobServiceContext.NO_PREFERRED_UID)) { + // This slot is free, and we haven't yet hit the limit on + // concurrent jobs... we can just throw the job in to here. + minPriorityContextId = j; + numActive++; + break; + } // No job on this context, but nextPending can't run here because - // the context has a preferred Uid. + // the context has a preferred Uid or we have reached the limit on + // concurrent jobs. continue; } if (job.getUid() != nextPending.getUid()) { @@ -1077,7 +1125,7 @@ public final class JobSchedulerService extends com.android.server.SystemService } if (minPriority > nextPending.lastEvaluatedPriority) { minPriority = nextPending.lastEvaluatedPriority; - minPriorityContextId = i; + minPriorityContextId = j; } } if (minPriorityContextId != -1) { @@ -1088,7 +1136,7 @@ public final class JobSchedulerService extends com.android.server.SystemService if (DEBUG) { Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final")); } - for (int i=0; i<mActiveServices.size(); i++) { + for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) { boolean preservePreferredUid = false; if (act[i]) { JobStatus js = mActiveServices.get(i).getRunningJob(); @@ -1329,7 +1377,7 @@ public final class JobSchedulerService extends com.android.server.SystemService public void process(JobStatus job) { pw.print(" Job #"); pw.print(index++); pw.print(": "); pw.println(job.toShortString()); - job.dump(pw, " "); + job.dump(pw, " ", true); pw.print(" Ready: "); pw.print(mHandler.isReadyToBeExecutedLocked(job)); pw.print(" (job="); @@ -1351,9 +1399,10 @@ public final class JobSchedulerService extends com.android.server.SystemService mControllers.get(i).dumpControllerStateLocked(pw); } pw.println(); - pw.println("Foreground uids:"); - for (int i=0; i<mForegroundUids.size(); i++) { - pw.print(" "); pw.println(UserHandle.formatUid(mForegroundUids.keyAt(i))); + pw.println("Uid priority overrides:"); + for (int i=0; i< mUidPriorityOverride.size(); i++) { + pw.print(" "); pw.print(UserHandle.formatUid(mUidPriorityOverride.keyAt(i))); + pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i)); } pw.println(); pw.println("Pending queue:"); @@ -1361,6 +1410,7 @@ public final class JobSchedulerService extends com.android.server.SystemService JobStatus job = mPendingJobs.get(i); pw.print(" Pending #"); pw.print(i); pw.print(": "); pw.println(job.toShortString()); + job.dump(pw, " ", false); int priority = evaluateJobPriorityLocked(job); if (priority != JobInfo.PRIORITY_DEFAULT) { pw.print(" Evaluated priority: "); pw.println(priority); @@ -1371,20 +1421,21 @@ public final class JobSchedulerService extends com.android.server.SystemService pw.println("Active jobs:"); for (int i=0; i<mActiveServices.size(); i++) { JobServiceContext jsc = mActiveServices.get(i); + pw.print(" Slot #"); pw.print(i); pw.print(": "); if (jsc.getRunningJob() == null) { + pw.println("inactive"); continue; } else { - final long timeout = jsc.getTimeoutElapsed(); - pw.print("Running for: "); - pw.print((now - jsc.getExecutionStartTimeElapsed())/1000); - pw.print("s timeout="); - pw.print(timeout); - pw.print(" fromnow="); - pw.println(timeout-now); - jsc.getRunningJob().dump(pw, " "); + pw.println(jsc.getRunningJob().toShortString()); + pw.print(" Running for: "); + TimeUtils.formatDuration(now - jsc.getExecutionStartTimeElapsed(), pw); + pw.print(", timeout at: "); + TimeUtils.formatDuration(jsc.getTimeoutElapsed() - now, pw); + pw.println(); + jsc.getRunningJob().dump(pw, " ", false); int priority = evaluateJobPriorityLocked(jsc.getRunningJob()); if (priority != JobInfo.PRIORITY_DEFAULT) { - pw.print(" Evaluated priority: "); pw.println(priority); + pw.print(" Evaluated priority: "); pw.println(priority); } } } @@ -1392,6 +1443,7 @@ public final class JobSchedulerService extends com.android.server.SystemService pw.print("mReadyToRock="); pw.println(mReadyToRock); pw.print("mDeviceIdleMode="); pw.println(mDeviceIdleMode); pw.print("mReportedActive="); pw.println(mReportedActive); + pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs); } pw.println(); } diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java index e6fbc39b6dc6..98bf8a9a8d08 100644 --- a/services/core/java/com/android/server/job/controllers/JobStatus.java +++ b/services/core/java/com/android/server/job/controllers/JobStatus.java @@ -134,6 +134,10 @@ public final class JobStatus { sb.append(job.getService().getPackageName()); sb.append('/'); sb.append(this.sourceTag); + if (sourcePackageName != null) { + sb.append('/'); + sb.append(this.sourcePackageName); + } this.batteryName = sb.toString(); } else { this.batteryName = job.getService().flattenToShortString(); @@ -433,10 +437,10 @@ public final class JobStatus { sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(" jId="); sb.append(job.getId()); - sb.append(" uid="); + sb.append(' '); UserHandle.formatUid(sb, callingUid); sb.append(' '); - sb.append(job.getService().flattenToShortString()); + sb.append(batteryName); return sb.toString(); } @@ -468,75 +472,71 @@ public final class JobStatus { } // Dumpsys infrastructure - public void dump(PrintWriter pw, String prefix) { + public void dump(PrintWriter pw, String prefix, boolean full) { pw.print(prefix); UserHandle.formatUid(pw, callingUid); pw.print(" tag="); pw.println(tag); pw.print(prefix); pw.print("Source: uid="); UserHandle.formatUid(pw, getSourceUid()); pw.print(" user="); pw.print(getSourceUserId()); pw.print(" pkg="); pw.println(getSourcePackageName()); - pw.print(prefix); pw.println("JobInfo:"); - pw.print(prefix); pw.print(" Service: "); - pw.println(job.getService().flattenToShortString()); - if (job.isPeriodic()) { - pw.print(prefix); pw.print(" PERIODIC: interval="); - TimeUtils.formatDuration(job.getIntervalMillis(), pw); - pw.print(" flex="); - TimeUtils.formatDuration(job.getFlexMillis(), pw); - pw.println(); - } - if (job.isPersisted()) { - pw.print(prefix); pw.println(" PERSISTED"); - } - if (job.getPriority() != 0) { - pw.print(prefix); pw.print(" Priority: "); - pw.println(job.getPriority()); - } - pw.print(prefix); pw.print(" Requires: charging="); - pw.print(job.isRequireCharging()); - pw.print(" deviceIdle="); - pw.println(job.isRequireDeviceIdle()); - if (job.getTriggerContentUris() != null) { - pw.print(prefix); pw.println(" Trigger content URIs:"); - for (int i=0; i<job.getTriggerContentUris().length; i++) { - JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i]; - pw.print(prefix); pw.print(" "); - pw.print(Integer.toHexString(trig.getFlags())); - pw.print(' ' ); - pw.println(trig.getUri()); + if (full) { + pw.print(prefix); pw.println("JobInfo:"); pw.print(prefix); + pw.print(" Service: "); pw.println(job.getService().flattenToShortString()); + if (job.isPeriodic()) { + pw.print(prefix); pw.print(" PERIODIC: interval="); + TimeUtils.formatDuration(job.getIntervalMillis(), pw); + pw.print(" flex="); TimeUtils.formatDuration(job.getFlexMillis(), pw); + pw.println(); } - } - if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) { - pw.print(prefix); pw.print(" Network type: "); - pw.println(job.getNetworkType()); - } - if (job.getMinLatencyMillis() != 0) { - pw.print(prefix); pw.print(" Minimum latency: "); - TimeUtils.formatDuration(job.getMinLatencyMillis(), pw); - pw.println(); - } - if (job.getMaxExecutionDelayMillis() != 0) { - pw.print(prefix); pw.print(" Max execution delay: "); - TimeUtils.formatDuration(job.getMaxExecutionDelayMillis(), pw); + if (job.isPersisted()) { + pw.print(prefix); pw.println(" PERSISTED"); + } + if (job.getPriority() != 0) { + pw.print(prefix); pw.print(" Priority: "); pw.println(job.getPriority()); + } + pw.print(prefix); pw.print(" Requires: charging="); + pw.print(job.isRequireCharging()); pw.print(" deviceIdle="); + pw.println(job.isRequireDeviceIdle()); + if (job.getTriggerContentUris() != null) { + pw.print(prefix); pw.println(" Trigger content URIs:"); + for (int i = 0; i < job.getTriggerContentUris().length; i++) { + JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i]; + pw.print(prefix); pw.print(" "); + pw.print(Integer.toHexString(trig.getFlags())); + pw.print(' '); pw.println(trig.getUri()); + } + } + if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) { + pw.print(prefix); pw.print(" Network type: "); pw.println(job.getNetworkType()); + } + if (job.getMinLatencyMillis() != 0) { + pw.print(prefix); pw.print(" Minimum latency: "); + TimeUtils.formatDuration(job.getMinLatencyMillis(), pw); + pw.println(); + } + if (job.getMaxExecutionDelayMillis() != 0) { + pw.print(prefix); pw.print(" Max execution delay: "); + TimeUtils.formatDuration(job.getMaxExecutionDelayMillis(), pw); + pw.println(); + } + pw.print(prefix); pw.print(" Backoff: policy="); pw.print(job.getBackoffPolicy()); + pw.print(" initial="); TimeUtils.formatDuration(job.getInitialBackoffMillis(), pw); pw.println(); - } - pw.print(prefix); pw.print(" Backoff: policy="); - pw.print(job.getBackoffPolicy()); - pw.print(" initial="); - TimeUtils.formatDuration(job.getInitialBackoffMillis(), pw); - pw.println(); - if (job.hasEarlyConstraint()) { - pw.print(prefix); pw.println(" Has early constraint"); - } - if (job.hasLateConstraint()) { - pw.print(prefix); pw.println(" Has late constraint"); + if (job.hasEarlyConstraint()) { + pw.print(prefix); pw.println(" Has early constraint"); + } + if (job.hasLateConstraint()) { + pw.print(prefix); pw.println(" Has late constraint"); + } } pw.print(prefix); pw.print("Required constraints:"); dumpConstraints(pw, requiredConstraints); pw.println(); - pw.print(prefix); pw.print("Satisfied constraints:"); - dumpConstraints(pw, satisfiedConstraints); - pw.println(); + if (full) { + pw.print(prefix); pw.print("Satisfied constraints:"); + dumpConstraints(pw, satisfiedConstraints); + pw.println(); + } if (changedAuthorities != null) { pw.print(prefix); pw.println("Changed authorities:"); for (int i=0; i<changedAuthorities.size(); i++) { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index c337c573ffb4..692e61092309 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -36,7 +36,7 @@ import static android.service.notification.NotificationListenerService.SUPPRESSE import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON; import static android.service.notification.NotificationListenerService.TRIM_FULL; import static android.service.notification.NotificationListenerService.TRIM_LIGHT; -import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH; +import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_DEFAULT; import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_NONE; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.END_TAG; @@ -2524,7 +2524,7 @@ public class NotificationManagerService extends SystemService { final Notification notification = record.sbn.getNotification(); // Should this notification make noise, vibe, or use the LED? - final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_HIGH; + final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_DEFAULT; final boolean canInterrupt = aboveThreshold && !record.isIntercepted(); if (DBG || record.isIntercepted()) Slog.v(TAG, diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index 6c5685df503d..fd893fa081a5 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -15,6 +15,7 @@ */ package com.android.server.notification; +import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_MIN; import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED; import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_DEFAULT; import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH; @@ -33,6 +34,8 @@ import android.os.Build; import android.os.UserHandle; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; +import android.util.Log; +import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.server.EventLogTags; @@ -55,6 +58,8 @@ import java.util.Objects; * {@hide} */ public final class NotificationRecord { + static final String TAG = "NotificationRecord"; + static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); final StatusBarNotification sbn; final int mOriginalFlags; private final Context mContext; @@ -123,6 +128,8 @@ public final class NotificationRecord { switch (n.priority) { case Notification.PRIORITY_MIN: + importance = IMPORTANCE_MIN; + break; case Notification.PRIORITY_LOW: importance = IMPORTANCE_LOW; break; @@ -143,25 +150,15 @@ public final class NotificationRecord { || n.sound != null || n.vibrate != null; stats.isNoisy = isNoisy; - if (!isNoisy && importance > IMPORTANCE_DEFAULT) { - importance = IMPORTANCE_DEFAULT; + + if (!isNoisy && importance > IMPORTANCE_LOW) { + importance = IMPORTANCE_LOW; } - try { - final ApplicationInfo applicationInfo = - mContext.getPackageManager().getApplicationInfoAsUser(sbn.getPackageName(), - 0, sbn.getUser().getIdentifier()); - if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.N) { - if (isNoisy) { - if (importance >= IMPORTANCE_HIGH) { - importance = IMPORTANCE_MAX; - } else { - importance = IMPORTANCE_HIGH; - } - } + if (isNoisy) { + if (importance < IMPORTANCE_DEFAULT) { + importance = IMPORTANCE_DEFAULT; } - } catch (NameNotFoundException e) { - // oh well. } if (n.fullScreenIntent != null) { diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java index 027285087de3..538f95146efc 100644 --- a/services/core/java/com/android/server/notification/NotificationUsageStats.java +++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java @@ -597,8 +597,9 @@ public class NotificationUsageStats { private static class ImportanceHistogram { // TODO define these somewhere else - private static final int NUM_IMPORTANCES = 5; - private static final String[] IMPORTANCE_NAMES = {"none", "low", "default", "high", "max"}; + private static final int NUM_IMPORTANCES = 6; + private static final String[] IMPORTANCE_NAMES = + {"none", "min", "low", "default", "high", "max"}; private final Context mContext; private final String[] mCounterNames; private final String mPrefix; diff --git a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java index f1fe3462432d..32501ad88789 100644 --- a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java @@ -15,12 +15,6 @@ */ package com.android.server.notification; -import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED; -import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH; -import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_LOW; -import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_MAX; -import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_NONE; - import org.mockito.Mock; import org.mockito.MockitoAnnotations; diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java index 9ac4dbfed65a..2a3f14340a39 100644 --- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java +++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java @@ -86,6 +86,122 @@ public class NotificationTestList extends TestActivity } private Test[] mTests = new Test[] { + new Test("Min priority") { + public void run() + { + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon2) + .setContentTitle("Min priority") + .setLights(0xff0000ff, 1, 0) + .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE) + .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + + getPackageName() + "/raw/ringer")) + .setPriority(Notification.PRIORITY_MIN) + .setFullScreenIntent(makeIntent2(), false) + .build(); + mNM.notify(7000, n); + } + }, + new Test("Min priority, high pri flag") { + public void run() + { + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon2) + .setContentTitle("Min priority, high pri flag") + .setLights(0xff0000ff, 1, 0) + .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE) + .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + + getPackageName() + "/raw/ringer")) + .setPriority(Notification.PRIORITY_MIN) + .setFullScreenIntent(makeIntent2(), true) + .build(); + mNM.notify(7001, n); + } + }, + new Test("Low priority") { + public void run() + { + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon2) + .setContentTitle("Low priority") + .setLights(0xff0000ff, 1, 0) + .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE) + .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + + getPackageName() + "/raw/ringer")) + .setPriority(Notification.PRIORITY_LOW) + .setFullScreenIntent(makeIntent2(), false) + .build(); + mNM.notify(7002, n); + } + }, + new Test("Default priority") { + public void run() + { + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon2) + .setContentTitle("Default priority") + .setLights(0xff0000ff, 1, 0) + .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE) + .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + + getPackageName() + "/raw/ringer")) + .setPriority(Notification.PRIORITY_DEFAULT) + .setFullScreenIntent(makeIntent2(), false) + .build(); + mNM.notify(7004, n); + } + }, + new Test("High priority") { + public void run() + { + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon2) + .setContentTitle("High priority") + .setLights(0xff0000ff, 1, 0) + .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE) + .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + + getPackageName() + "/raw/ringer")) + .setPriority(Notification.PRIORITY_HIGH) + .setFullScreenIntent(makeIntent2(), false) + .build(); + mNM.notify(7006, n); + } + }, + new Test("Max priority") { + public void run() + { + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon2) + .setContentTitle("Max priority") + .setLights(0xff0000ff, 1, 0) + .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE) + .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + + getPackageName() + "/raw/ringer")) + .setPriority(Notification.PRIORITY_MAX) + .setFullScreenIntent(makeIntent2(), false) + .build(); + mNM.notify(7008, n); + } + }, + new Test("Max priority with delay") { + public void run() + { + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + } + Notification n = new Notification.Builder(NotificationTestList.this) + .setSmallIcon(R.drawable.icon2) + .setContentTitle("Max priority") + .setLights(0xff0000ff, 1, 0) + .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE) + .setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + + getPackageName() + "/raw/ringer")) + .setPriority(Notification.PRIORITY_MAX) + .setFullScreenIntent(makeIntent2(), false) + .build(); + mNM.notify(7008, n); + } + }, new Test("Off") { public void run() { PowerManager pm = (PowerManager)NotificationTestList.this.getSystemService(Context.POWER_SERVICE); |