Merge changes from topic 'keyguard_refactor'
* changes:
The big keyguard transition refactor (6/n)
The big keyguard transition refactor (5/n)
The big keyguard transition refactor (4/n)
The big keyguard transition refactor (3/n)
The big keyguard transition refactor (2/n)
The big keyguard transition refactor (1/n)
diff --git a/Android.mk b/Android.mk
index db63739..b34d2ff 100644
--- a/Android.mk
+++ b/Android.mk
@@ -118,9 +118,10 @@
core/java/android/bluetooth/IBluetoothPan.aidl \
core/java/android/bluetooth/IBluetoothManager.aidl \
core/java/android/bluetooth/IBluetoothManagerCallback.aidl \
+ core/java/android/bluetooth/IBluetoothMap.aidl \
+ core/java/android/bluetooth/IBluetoothMapClient.aidl \
core/java/android/bluetooth/IBluetoothPbap.aidl \
core/java/android/bluetooth/IBluetoothPbapClient.aidl \
- core/java/android/bluetooth/IBluetoothMap.aidl \
core/java/android/bluetooth/IBluetoothSap.aidl \
core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl \
core/java/android/bluetooth/IBluetoothHeadsetClient.aidl \
diff --git a/api/current.txt b/api/current.txt
index 643240c..5077168 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5335,16 +5335,20 @@
}
public final class NotificationChannel implements android.os.Parcelable {
- ctor public NotificationChannel(java.lang.String, java.lang.CharSequence);
+ ctor public NotificationChannel(java.lang.String, java.lang.CharSequence, int);
ctor protected NotificationChannel(android.os.Parcel);
method public boolean canBypassDnd();
method public int describeContents();
- method public android.net.Uri getDefaultRingtone();
method public java.lang.String getId();
method public int getImportance();
+ method public int getLockscreenVisibility();
method public java.lang.CharSequence getName();
- method public void setDefaultRingtone(android.net.Uri);
+ method public android.net.Uri getRingtone();
+ method public void setBypassDnd(boolean);
+ method public void setImportance(int);
method public void setLights(boolean);
+ method public void setLockscreenVisibility(int);
+ method public void setRingtone(android.net.Uri);
method public void setVibration(boolean);
method public boolean shouldShowLights();
method public boolean shouldVibrate();
@@ -5376,7 +5380,6 @@
method public final void setInterruptionFilter(int);
method public void setNotificationPolicy(android.app.NotificationManager.Policy);
method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
- method public void updateNotificationChannel(android.app.NotificationChannel);
field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED";
diff --git a/api/system-current.txt b/api/system-current.txt
index d231154..35024b7 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5481,22 +5481,23 @@
}
public final class NotificationChannel implements android.os.Parcelable {
- ctor public NotificationChannel(java.lang.String, java.lang.CharSequence);
+ ctor public NotificationChannel(java.lang.String, java.lang.CharSequence, int);
ctor protected NotificationChannel(android.os.Parcel);
method public boolean canBypassDnd();
method public int describeContents();
- method public android.net.Uri getDefaultRingtone();
method public java.lang.String getId();
method public int getImportance();
method public int getLockscreenVisibility();
method public java.lang.CharSequence getName();
+ method public android.net.Uri getRingtone();
+ method public int getUserLockedFields();
+ method public void lockFields(int);
method public void populateFromXml(org.xmlpull.v1.XmlPullParser);
method public void setBypassDnd(boolean);
- method public void setDefaultRingtone(android.net.Uri);
method public void setImportance(int);
method public void setLights(boolean);
method public void setLockscreenVisibility(int);
- method public void setName(java.lang.CharSequence);
+ method public void setRingtone(android.net.Uri);
method public void setVibration(boolean);
method public boolean shouldShowLights();
method public boolean shouldVibrate();
@@ -5505,6 +5506,12 @@
method public void writeXml(org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
field public static final android.os.Parcelable.Creator<android.app.NotificationChannel> CREATOR;
field public static final java.lang.String DEFAULT_CHANNEL_ID = "miscellaneous";
+ field public static final int USER_LOCKED_IMPORTANCE = 4; // 0x4
+ field public static final int USER_LOCKED_LIGHTS = 8; // 0x8
+ field public static final int USER_LOCKED_PRIORITY = 1; // 0x1
+ field public static final int USER_LOCKED_RINGTONE = 32; // 0x20
+ field public static final int USER_LOCKED_VIBRATION = 16; // 0x10
+ field public static final int USER_LOCKED_VISIBILITY = 2; // 0x2
}
public class NotificationManager {
@@ -5530,7 +5537,6 @@
method public final void setInterruptionFilter(int);
method public void setNotificationPolicy(android.app.NotificationManager.Policy);
method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
- method public void updateNotificationChannel(android.app.NotificationChannel);
field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED";
diff --git a/api/test-current.txt b/api/test-current.txt
index 3021b25..acb74e7 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5338,16 +5338,20 @@
}
public final class NotificationChannel implements android.os.Parcelable {
- ctor public NotificationChannel(java.lang.String, java.lang.CharSequence);
+ ctor public NotificationChannel(java.lang.String, java.lang.CharSequence, int);
ctor protected NotificationChannel(android.os.Parcel);
method public boolean canBypassDnd();
method public int describeContents();
- method public android.net.Uri getDefaultRingtone();
method public java.lang.String getId();
method public int getImportance();
+ method public int getLockscreenVisibility();
method public java.lang.CharSequence getName();
- method public void setDefaultRingtone(android.net.Uri);
+ method public android.net.Uri getRingtone();
+ method public void setBypassDnd(boolean);
+ method public void setImportance(int);
method public void setLights(boolean);
+ method public void setLockscreenVisibility(int);
+ method public void setRingtone(android.net.Uri);
method public void setVibration(boolean);
method public boolean shouldShowLights();
method public boolean shouldVibrate();
@@ -5379,7 +5383,6 @@
method public final void setInterruptionFilter(int);
method public void setNotificationPolicy(android.app.NotificationManager.Policy);
method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
- method public void updateNotificationChannel(android.app.NotificationChannel);
field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED";
diff --git a/cmds/am/am b/cmds/am/am
index 1d426bc..54c2d39 100755
--- a/cmds/am/am
+++ b/cmds/am/am
@@ -1,9 +1,9 @@
#!/system/bin/sh
-#
-# Script to start "am" on the device, which has a very rudimentary
-# shell.
-#
-base=/system
-export CLASSPATH=$base/framework/am.jar
-exec app_process $base/bin com.android.commands.am.Am "$@"
+if [ "$1" != "instrument" ] ; then
+ cmd activity "$@"
+else
+ base=/system
+ export CLASSPATH=$base/framework/am.jar
+ exec app_process $base/bin com.android.commands.am.Am "$@"
+fi
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 91a4549..470a0fa 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -84,34 +84,9 @@
public class Am extends BaseCommand {
- private static final String SHELL_PACKAGE_NAME = "com.android.shell";
-
- // Is the object moving in a positive direction?
- private static final boolean MOVING_FORWARD = true;
- // Is the object moving in the horizontal plan?
- private static final boolean MOVING_HORIZONTALLY = true;
- // Is the object current point great then its target point?
- private static final boolean GREATER_THAN_TARGET = true;
- // Amount we reduce the stack size by when testing a task re-size.
- private static final int STACK_BOUNDS_INSET = 10;
-
- public static final String NO_CLASS_ERROR_CODE = "Error type 3";
private IActivityManager mAm;
private IPackageManager mPm;
- private int mStartFlags = 0;
- private boolean mWaitOption = false;
- private boolean mStopOption = false;
-
- private int mRepeat = 0;
- private int mUserId;
- private String mReceiverPermission;
-
- private String mProfileFile;
- private int mSamplingInterval;
- private boolean mAutoStop;
- private int mStackId;
-
/**
* Command-line entry point.
*
@@ -123,249 +98,11 @@
@Override
public void onShowUsage(PrintStream out) {
- PrintWriter pw = new PrintWriter(out);
- pw.println(
- "usage: am [subcommand] [options]\n" +
- "usage: am start [-D] [-N] [-W] [-P <FILE>] [--start-profiler <FILE>]\n" +
- " [--sampling INTERVAL] [-R COUNT] [-S]\n" +
- " [--track-allocation] [--user <USER_ID> | current] <INTENT>\n" +
- " am startservice [--user <USER_ID> | current] <INTENT>\n" +
- " am stopservice [--user <USER_ID> | current] <INTENT>\n" +
- " am force-stop [--user <USER_ID> | all | current] <PACKAGE>\n" +
- " am kill [--user <USER_ID> | all | current] <PACKAGE>\n" +
- " am kill-all\n" +
- " am broadcast [--user <USER_ID> | all | current] <INTENT>\n" +
- " am instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]\n" +
- " [--user <USER_ID> | current]\n" +
- " [--no-window-animation] [--abi <ABI>] <COMPONENT>\n" +
- " am profile start [--user <USER_ID> current] [--sampling INTERVAL] <PROCESS> <FILE>\n" +
- " am profile stop [--user <USER_ID> current] [<PROCESS>]\n" +
- " am dumpheap [--user <USER_ID> current] [-n] <PROCESS> <FILE>\n" +
- " am set-debug-app [-w] [--persistent] <PACKAGE>\n" +
- " am clear-debug-app\n" +
- " am set-watch-heap <PROCESS> <MEM-LIMIT>\n" +
- " am clear-watch-heap\n" +
- " am bug-report [--progress]\n" +
- " am monitor [--gdb <port>]\n" +
- " am hang [--allow-restart]\n" +
- " am restart\n" +
- " am idle-maintenance\n" +
- " am screen-compat [on|off] <PACKAGE>\n" +
- " am package-importance <PACKAGE>\n" +
- " am to-uri [INTENT]\n" +
- " am to-intent-uri [INTENT]\n" +
- " am to-app-uri [INTENT]\n" +
- " am switch-user <USER_ID>\n" +
- " am start-user <USER_ID>\n" +
- " am unlock-user <USER_ID> [TOKEN_HEX]\n" +
- " am stop-user [-w] [-f] <USER_ID>\n" +
- " am stack start <DISPLAY_ID> <INTENT>\n" +
- " am stack movetask <TASK_ID> <STACK_ID> [true|false]\n" +
- " am stack resize <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
- " am stack resize-animated <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
- " am stack resize-docked-stack <LEFT,TOP,RIGHT,BOTTOM> [<TASK_LEFT,TASK_TOP,TASK_RIGHT,TASK_BOTTOM>]\n" +
- " am stack size-docked-stack-test: <STEP_SIZE> <l|t|r|b> [DELAY_MS]\n" +
- " am stack move-top-activity-to-pinned-stack: <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
- " am stack positiontask <TASK_ID> <STACK_ID> <POSITION>\n" +
- " am stack list\n" +
- " am stack info <STACK_ID>\n" +
- " am stack remove <STACK_ID>\n" +
- " am task lock <TASK_ID>\n" +
- " am task lock stop\n" +
- " am task resizeable <TASK_ID> [0 (unresizeable) | 1 (crop_windows) | 2 (resizeable) | 3 (resizeable_and_pipable)]\n" +
- " am task resize <TASK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
- " am task drag-task-test <TASK_ID> <STEP_SIZE> [DELAY_MS] \n" +
- " am task size-task-test <TASK_ID> <STEP_SIZE> [DELAY_MS] \n" +
- " am get-config\n" +
- " am suppress-resize-config-changes <true|false>\n" +
- " am set-inactive [--user <USER_ID>] <PACKAGE> true|false\n" +
- " am get-inactive [--user <USER_ID>] <PACKAGE>\n" +
- " am send-trim-memory [--user <USER_ID>] <PROCESS>\n" +
- " [HIDDEN|RUNNING_MODERATE|BACKGROUND|RUNNING_LOW|MODERATE|RUNNING_CRITICAL|COMPLETE]\n" +
- " am get-current-user\n" +
- "\n" +
- "am start: start an Activity. Options are:\n" +
- " -D: enable debugging\n" +
- " -N: enable native debugging\n" +
- " -W: wait for launch to complete\n" +
- " --start-profiler <FILE>: start profiler and send results to <FILE>\n" +
- " --sampling INTERVAL: use sample profiling with INTERVAL microseconds\n" +
- " between samples (use with --start-profiler)\n" +
- " -P <FILE>: like above, but profiling stops when app goes idle\n" +
- " -R: repeat the activity launch <COUNT> times. Prior to each repeat,\n" +
- " the top activity will be finished.\n" +
- " -S: force stop the target app before starting the activity\n" +
- " --track-allocation: enable tracking of object allocations\n" +
- " --user <USER_ID> | current: Specify which user to run as; if not\n" +
- " specified then run as the current user.\n" +
- " --stack <STACK_ID>: Specify into which stack should the activity be put.\n" +
- "\n" +
- "am startservice: start a Service. Options are:\n" +
- " --user <USER_ID> | current: Specify which user to run as; if not\n" +
- " specified then run as the current user.\n" +
- "\n" +
- "am stopservice: stop a Service. Options are:\n" +
- " --user <USER_ID> | current: Specify which user to run as; if not\n" +
- " specified then run as the current user.\n" +
- "\n" +
- "am force-stop: force stop everything associated with <PACKAGE>.\n" +
- " --user <USER_ID> | all | current: Specify user to force stop;\n" +
- " all users if not specified.\n" +
- "\n" +
- "am kill: Kill all processes associated with <PACKAGE>. Only kills.\n" +
- " processes that are safe to kill -- that is, will not impact the user\n" +
- " experience.\n" +
- " --user <USER_ID> | all | current: Specify user whose processes to kill;\n" +
- " all users if not specified.\n" +
- "\n" +
- "am kill-all: Kill all background processes.\n" +
- "\n" +
- "am broadcast: send a broadcast Intent. Options are:\n" +
- " --user <USER_ID> | all | current: Specify which user to send to; if not\n" +
- " specified then send to all users.\n" +
- " --receiver-permission <PERMISSION>: Require receiver to hold permission.\n" +
- "\n" +
- "am instrument: start an Instrumentation. Typically this target <COMPONENT>\n" +
- " is the form <TEST_PACKAGE>/<RUNNER_CLASS> or only <TEST_PACKAGE> if there \n" +
- " is only one instrumentation. Options are:\n" +
- " -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT). Use with\n" +
- " [-e perf true] to generate raw output for performance measurements.\n" +
- " -e <NAME> <VALUE>: set argument <NAME> to <VALUE>. For test runners a\n" +
- " common form is [-e <testrunner_flag> <value>[,<value>...]].\n" +
- " -p <FILE>: write profiling data to <FILE>\n" +
- " -m: Write output as protobuf (machine readable)\n" +
- " -w: wait for instrumentation to finish before returning. Required for\n" +
- " test runners.\n" +
- " --user <USER_ID> | current: Specify user instrumentation runs in;\n" +
- " current user if not specified.\n" +
- " --no-window-animation: turn off window animations while running.\n" +
- " --abi <ABI>: Launch the instrumented process with the selected ABI.\n" +
- " This assumes that the process supports the selected ABI.\n" +
- "\n" +
- "am trace-ipc: Trace IPC transactions.\n" +
- " start: start tracing IPC transactions.\n" +
- " stop: stop tracing IPC transactions and dump the results to file.\n" +
- " --dump-file <FILE>: Specify the file the trace should be dumped to.\n" +
- "\n" +
- "am profile: start and stop profiler on a process. The given <PROCESS> argument\n" +
- " may be either a process name or pid. Options are:\n" +
- " --user <USER_ID> | current: When supplying a process name,\n" +
- " specify user of process to profile; uses current user if not specified.\n" +
- "\n" +
- "am dumpheap: dump the heap of a process. The given <PROCESS> argument may\n" +
- " be either a process name or pid. Options are:\n" +
- " -n: dump native heap instead of managed heap\n" +
- " --user <USER_ID> | current: When supplying a process name,\n" +
- " specify user of process to dump; uses current user if not specified.\n" +
- "\n" +
- "am set-debug-app: set application <PACKAGE> to debug. Options are:\n" +
- " -w: wait for debugger when application starts\n" +
- " --persistent: retain this value\n" +
- "\n" +
- "am clear-debug-app: clear the previously set-debug-app.\n" +
- "\n" +
- "am set-watch-heap: start monitoring pss size of <PROCESS>, if it is at or\n" +
- " above <HEAP-LIMIT> then a heap dump is collected for the user to report\n" +
- "\n" +
- "am clear-watch-heap: clear the previously set-watch-heap.\n" +
- "\n" +
- "am bug-report: request bug report generation; will launch a notification\n" +
- " when done to select where it should be delivered. Options are: \n" +
- " --progress: will launch a notification right away to show its progress.\n" +
- "\n" +
- "am monitor: start monitoring for crashes or ANRs.\n" +
- " --gdb: start gdbserv on the given port at crash/ANR\n" +
- "\n" +
- "am hang: hang the system.\n" +
- " --allow-restart: allow watchdog to perform normal system restart\n" +
- "\n" +
- "am restart: restart the user-space system.\n" +
- "\n" +
- "am idle-maintenance: perform idle maintenance now.\n" +
- "\n" +
- "am screen-compat: control screen compatibility mode of <PACKAGE>.\n" +
- "\n" +
- "am package-importance: print current importance of <PACKAGE>.\n" +
- "\n" +
- "am to-uri: print the given Intent specification as a URI.\n" +
- "\n" +
- "am to-intent-uri: print the given Intent specification as an intent: URI.\n" +
- "\n" +
- "am to-app-uri: print the given Intent specification as an android-app: URI.\n" +
- "\n" +
- "am switch-user: switch to put USER_ID in the foreground, starting\n" +
- " execution of that user if it is currently stopped.\n" +
- "\n" +
- "am start-user: start USER_ID in background if it is currently stopped,\n" +
- " use switch-user if you want to start the user in foreground.\n" +
- "\n" +
- "am stop-user: stop execution of USER_ID, not allowing it to run any\n" +
- " code until a later explicit start or switch to it.\n" +
- " -w: wait for stop-user to complete.\n" +
- " -f: force stop even if there are related users that cannot be stopped.\n" +
- "\n" +
- "am stack start: start a new activity on <DISPLAY_ID> using <INTENT>.\n" +
- "\n" +
- "am stack movetask: move <TASK_ID> from its current stack to the top (true) or" +
- " bottom (false) of <STACK_ID>.\n" +
- "\n" +
- "am stack resize: change <STACK_ID> size and position to <LEFT,TOP,RIGHT,BOTTOM>.\n" +
- "\n" +
- "am stack resize-docked-stack: change docked stack to <LEFT,TOP,RIGHT,BOTTOM>\n" +
- " and supplying temporary different task bounds indicated by\n" +
- " <TASK_LEFT,TOP,RIGHT,BOTTOM>\n" +
- "\n" +
- "am stack size-docked-stack-test: test command for sizing docked stack by\n" +
- " <STEP_SIZE> increments from the side <l>eft, <t>op, <r>ight, or <b>ottom\n" +
- " applying the optional [DELAY_MS] between each step.\n" +
- "\n" +
- "am stack move-top-activity-to-pinned-stack: moves the top activity from\n" +
- " <STACK_ID> to the pinned stack using <LEFT,TOP,RIGHT,BOTTOM> for the\n" +
- " bounds of the pinned stack.\n" +
- "\n" +
- "am stack positiontask: place <TASK_ID> in <STACK_ID> at <POSITION>" +
- "\n" +
- "am stack list: list all of the activity stacks and their sizes.\n" +
- "\n" +
- "am stack info: display the information about activity stack <STACK_ID>.\n" +
- "\n" +
- "am stack remove: remove stack <STACK_ID>.\n" +
- "\n" +
- "am task lock: bring <TASK_ID> to the front and don't allow other tasks to run.\n" +
- "\n" +
- "am task lock stop: end the current task lock.\n" +
- "\n" +
- "am task resizeable: change resizeable mode of <TASK_ID>.\n" +
- " 0 (unresizeable) | 1 (crop_windows) | 2 (resizeable) | 3 (resizeable_and_pipable)\n" +
- "\n" +
- "am task resize: makes sure <TASK_ID> is in a stack with the specified bounds.\n" +
- " Forces the task to be resizeable and creates a stack if no existing stack\n" +
- " has the specified bounds.\n" +
- "\n" +
- "am task drag-task-test: test command for dragging/moving <TASK_ID> by\n" +
- " <STEP_SIZE> increments around the screen applying the optional [DELAY_MS]\n" +
- " between each step.\n" +
- "\n" +
- "am task size-task-test: test command for sizing <TASK_ID> by <STEP_SIZE>" +
- " increments within the screen applying the optional [DELAY_MS] between\n" +
- " each step.\n" +
- "\n" +
- "am get-config: retrieve the configuration and any recent configurations\n" +
- " of the device.\n" +
- "am suppress-resize-config-changes: suppresses configuration changes due to\n" +
- " user resizing an activity/task.\n" +
- "\n" +
- "am set-inactive: sets the inactive state of an app.\n" +
- "\n" +
- "am get-inactive: returns the inactive state of an app.\n" +
- "\n" +
- "am send-trim-memory: send a memory trim event to a <PROCESS>.\n" +
- "\n" +
- "am get-current-user: returns id of the current foreground user.\n" +
- "\n"
- );
- Intent.printIntentArgsHelp(pw, "");
- pw.flush();
+ try {
+ runAmCmd(new String[] { "help" });
+ } catch (AndroidException e) {
+ e.printStackTrace(System.err);
+ }
}
@Override
@@ -385,9 +122,7 @@
String op = nextArgRequired();
- if (op.equals("broadcast")) {
- sendBroadcast();
- } else if (op.equals("instrument")) {
+ if (op.equals("instrument")) {
runInstrument();
} else {
runAmCmd(getRawArgs());
@@ -458,90 +193,6 @@
}
}
- private Intent makeIntent(int defUser) throws URISyntaxException {
- mStartFlags = 0;
- mWaitOption = false;
- mStopOption = false;
- mRepeat = 0;
- mProfileFile = null;
- mSamplingInterval = 0;
- mAutoStop = false;
- mUserId = defUser;
- mStackId = INVALID_STACK_ID;
-
- return Intent.parseCommandArgs(mArgs, new Intent.CommandOptionHandler() {
- @Override
- public boolean handleOption(String opt, ShellCommand cmd) {
- if (opt.equals("-D")) {
- mStartFlags |= ActivityManager.START_FLAG_DEBUG;
- } else if (opt.equals("-N")) {
- mStartFlags |= ActivityManager.START_FLAG_NATIVE_DEBUGGING;
- } else if (opt.equals("-W")) {
- mWaitOption = true;
- } else if (opt.equals("-P")) {
- mProfileFile = nextArgRequired();
- mAutoStop = true;
- } else if (opt.equals("--start-profiler")) {
- mProfileFile = nextArgRequired();
- mAutoStop = false;
- } else if (opt.equals("--sampling")) {
- mSamplingInterval = Integer.parseInt(nextArgRequired());
- } else if (opt.equals("-R")) {
- mRepeat = Integer.parseInt(nextArgRequired());
- } else if (opt.equals("-S")) {
- mStopOption = true;
- } else if (opt.equals("--track-allocation")) {
- mStartFlags |= ActivityManager.START_FLAG_TRACK_ALLOCATION;
- } else if (opt.equals("--user")) {
- mUserId = UserHandle.parseUserArg(nextArgRequired());
- } else if (opt.equals("--receiver-permission")) {
- mReceiverPermission = nextArgRequired();
- } else if (opt.equals("--stack")) {
- mStackId = Integer.parseInt(nextArgRequired());
- } else {
- return false;
- }
- return true;
- }
- });
- }
-
- private class IntentReceiver extends IIntentReceiver.Stub {
- private boolean mFinished = false;
-
- @Override
- public void performReceive(Intent intent, int resultCode, String data, Bundle extras,
- boolean ordered, boolean sticky, int sendingUser) {
- String line = "Broadcast completed: result=" + resultCode;
- if (data != null) line = line + ", data=\"" + data + "\"";
- if (extras != null) line = line + ", extras: " + extras;
- System.out.println(line);
- synchronized (this) {
- mFinished = true;
- notifyAll();
- }
- }
-
- public synchronized void waitForFinish() {
- try {
- while (!mFinished) wait();
- } catch (InterruptedException e) {
- throw new IllegalStateException(e);
- }
- }
- }
-
- private void sendBroadcast() throws Exception {
- Intent intent = makeIntent(UserHandle.USER_CURRENT);
- IntentReceiver receiver = new IntentReceiver();
- String[] requiredPermissions = mReceiverPermission == null ? null
- : new String[] {mReceiverPermission};
- System.out.println("Broadcasting: " + intent);
- mAm.broadcastIntent(null, intent, null, receiver, 0, null, null, requiredPermissions,
- android.app.AppOpsManager.OP_NONE, null, true, false, mUserId);
- receiver.waitForFinish();
- }
-
public void runInstrument() throws Exception {
Instrument instrument = new Instrument(mAm, mPm);
diff --git a/cmds/appops/appops b/cmds/appops/appops
index 25d2031..5dc85aa 100755
--- a/cmds/appops/appops
+++ b/cmds/appops/appops
@@ -1 +1,2 @@
-cmd appops $@
+#!/system/bin/sh
+cmd appops "$@"
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index c87eef9..ee81c58 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -59,7 +59,6 @@
int getPackageImportance(String pkg);
void createNotificationChannel(String pkg, in NotificationChannel channel);
- void updateNotificationChannel(String pkg, in NotificationChannel channel);
void updateNotificationChannelForPackage(String pkg, int uid, in NotificationChannel channel);
NotificationChannel getNotificationChannel(String pkg, String channelId);
NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId);
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 4150172..f259c6d 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -1,3 +1,18 @@
+/*
+ * 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.
+ */
package android.app;
import org.json.JSONException;
@@ -33,12 +48,45 @@
private static final String ATT_IMPORTANCE = "importance";
private static final String ATT_LIGHTS = "lights";
private static final String ATT_VIBRATION = "vibration";
- private static final String ATT_DEFAULT_RINGTONE = "ringtone";
+ private static final String ATT_RINGTONE = "ringtone";
+ private static final String ATT_USER_APPROVED = "approved";
+ private static final String ATT_USER_LOCKED = "locked";
+
+ /**
+ * @hide
+ */
+ @SystemApi
+ public static final int USER_LOCKED_PRIORITY = 0x00000001;
+ /**
+ * @hide
+ */
+ @SystemApi
+ public static final int USER_LOCKED_VISIBILITY = 0x00000002;
+ /**
+ * @hide
+ */
+ @SystemApi
+ public static final int USER_LOCKED_IMPORTANCE = 0x00000004;
+ /**
+ * @hide
+ */
+ @SystemApi
+ public static final int USER_LOCKED_LIGHTS = 0x00000008;
+ /**
+ * @hide
+ */
+ @SystemApi
+ public static final int USER_LOCKED_VIBRATION = 0x00000010;
+ /**
+ * @hide
+ */
+ @SystemApi
+ public static final int USER_LOCKED_RINGTONE = 0x00000020;
private static final int DEFAULT_VISIBILITY =
- NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
+ NotificationManager.VISIBILITY_NO_OVERRIDE;
private static final int DEFAULT_IMPORTANCE =
- NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
+ NotificationManager.IMPORTANCE_UNSPECIFIED;
private final String mId;
private CharSequence mName;
@@ -48,16 +96,21 @@
private Uri mRingtone;
private boolean mLights;
private boolean mVibration;
+ private int mUserLockedFields;
/**
* Creates a notification channel.
*
* @param id The id of the channel. Must be unique per package.
* @param name The user visible name of the channel.
+ * @param importance The importance of the channel. This controls how interruptive notifications
+ * posted to this channel are. See e.g.
+ * {@link NotificationManager#IMPORTANCE_DEFAULT}.
*/
- public NotificationChannel(String id, CharSequence name) {
+ public NotificationChannel(String id, CharSequence name, int importance) {
this.mId = id;
this.mName = name;
+ this.mImportance = importance;
}
protected NotificationChannel(Parcel in) {
@@ -77,6 +130,7 @@
}
mLights = in.readByte() != 0;
mVibration = in.readByte() != 0;
+ mUserLockedFields = in.readInt();
}
@Override
@@ -99,54 +153,67 @@
}
dest.writeByte(mLights ? (byte) 1 : (byte) 0);
dest.writeByte(mVibration ? (byte) 1 : (byte) 0);
- }
-
- // Only modifiable by users.
- /**
- * @hide
- */
- @SystemApi
- public void setName(CharSequence name) {
- this.mName = name;
+ dest.writeInt(mUserLockedFields);
}
/**
* @hide
*/
@SystemApi
- public void setImportance(int importance) {
- this.mImportance = importance;
+ public void lockFields(int field) {
+ mUserLockedFields |= field;
}
+ // Modifiable by a notification ranker.
+
/**
- * @hide
+ * Only modifiable by the system and notification ranker.
+ *
+ * Sets whether or not this notification can interrupt the user in
+ * {@link android.app.NotificationManager.Policy#INTERRUPTION_FILTER_PRIORITY} mode.
*/
- @SystemApi
public void setBypassDnd(boolean bypassDnd) {
this.mBypassDnd = bypassDnd;
}
/**
- * @hide
+ * Only modifiable by the system and notification ranker.
+ *
+ * Sets whether this notification appears on the lockscreen or not, and if so, whether it
+ * appears in a redacted form. See e.g. {@link Notification#VISIBILITY_SECRET}.
*/
- @SystemApi
public void setLockscreenVisibility(int lockscreenVisibility) {
this.mLockscreenVisibility = lockscreenVisibility;
}
+ /**
+ * Only modifiable by the system and notification ranker.
+ *
+ * Sets the level of interruption of this notification channel.
+ *
+ * @param importance the amount the user should be interrupted by notifications from this
+ * channel. See e.g.
+ * {@link android.app.NotificationManager#IMPORTANCE_DEFAULT}.
+ */
+ public void setImportance(int importance) {
+ this.mImportance = importance;
+ }
+
// Modifiable by apps on channel creation.
/**
* Sets the ringtone that should be played for notifications posted to this channel if
- * the notifications don't supply a ringtone. Only modifiable on channel creation.
+ * the notifications don't supply a ringtone. Only modifiable before the channel is submitted
+ * to the NotificationManager.
*/
- public void setDefaultRingtone(Uri defaultRingtone) {
- this.mRingtone = defaultRingtone;
+ public void setRingtone(Uri ringtone) {
+ this.mRingtone = ringtone;
}
/**
* Sets whether notifications posted to this channel should display notification lights,
- * on devices that support that feature. Only modifiable on channel creation.
+ * on devices that support that feature. Only modifiable before the channel is submitted to
+ * the NotificationManager.
*/
public void setLights(boolean lights) {
this.mLights = lights;
@@ -154,7 +221,8 @@
/**
* Sets whether notification posted to this channel should vibrate, even if individual
- * notifications are marked as having vibration only modifiable on channel creation.
+ * notifications are marked as having vibration only modifiable before the channel is submitted
+ * to the NotificationManager.
*/
public void setVibration(boolean vibration) {
this.mVibration = vibration;
@@ -193,7 +261,7 @@
/**
* Returns the notification sound for this channel.
*/
- public Uri getDefaultRingtone() {
+ public Uri getRingtone() {
return mRingtone;
}
@@ -212,9 +280,9 @@
}
/**
- * @hide
+ * Returns whether or not notifications posted to this channel are shown on the lockscreen in
+ * full or redacted form.
*/
- @SystemApi
public int getLockscreenVisibility() {
return mLockscreenVisibility;
}
@@ -223,15 +291,23 @@
* @hide
*/
@SystemApi
+ public int getUserLockedFields() {
+ return mUserLockedFields;
+ }
+
+ /**
+ * @hide
+ */
+ @SystemApi
public void populateFromXml(XmlPullParser parser) {
- // Name and id are set in the constructor.
- setImportance(safeInt(parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE));
+ // Name, id, and importance are set in the constructor.
setBypassDnd(Notification.PRIORITY_DEFAULT
!= safeInt(parser, ATT_PRIORITY, Notification.PRIORITY_DEFAULT));
setLockscreenVisibility(safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY));
- setDefaultRingtone(safeUri(parser, ATT_DEFAULT_RINGTONE));
+ setRingtone(safeUri(parser, ATT_RINGTONE));
setLights(safeBool(parser, ATT_LIGHTS, false));
setVibration(safeBool(parser, ATT_VIBRATION, false));
+ lockFields(safeInt(parser, ATT_USER_LOCKED, 0));
}
/**
@@ -254,8 +330,8 @@
out.attribute(null, ATT_VISIBILITY,
Integer.toString(getLockscreenVisibility()));
}
- if (getDefaultRingtone() != null) {
- out.attribute(null, ATT_DEFAULT_RINGTONE, getDefaultRingtone().toString());
+ if (getRingtone() != null) {
+ out.attribute(null, ATT_RINGTONE, getRingtone().toString());
}
if (shouldShowLights()) {
out.attribute(null, ATT_LIGHTS, Boolean.toString(shouldShowLights()));
@@ -263,6 +339,10 @@
if (shouldVibrate()) {
out.attribute(null, ATT_VIBRATION, Boolean.toString(shouldVibrate()));
}
+ if (getUserLockedFields() != 0) {
+ out.attribute(null, ATT_USER_LOCKED, Integer.toString(getUserLockedFields()));
+ }
+
out.endTag(null, TAG_CHANNEL);
}
@@ -284,11 +364,12 @@
if (getLockscreenVisibility() != DEFAULT_VISIBILITY) {
record.put(ATT_VISIBILITY, Notification.visibilityToString(getLockscreenVisibility()));
}
- if (getDefaultRingtone() != null) {
- record.put(ATT_DEFAULT_RINGTONE, getDefaultRingtone().toString());
+ if (getRingtone() != null) {
+ record.put(ATT_RINGTONE, getRingtone().toString());
}
record.put(ATT_LIGHTS, Boolean.toString(shouldShowLights()));
record.put(ATT_VIBRATION, Boolean.toString(shouldVibrate()));
+ record.put(ATT_USER_LOCKED, Integer.toString(getUserLockedFields()));
return record;
}
@@ -342,15 +423,32 @@
NotificationChannel that = (NotificationChannel) o;
- if (mImportance != that.mImportance) return false;
+ if (getImportance() != that.getImportance()) return false;
if (mBypassDnd != that.mBypassDnd) return false;
- if (mLockscreenVisibility != that.mLockscreenVisibility) return false;
+ if (getLockscreenVisibility() != that.getLockscreenVisibility()) return false;
if (mLights != that.mLights) return false;
if (mVibration != that.mVibration) return false;
- if (!mId.equals(that.mId)) return false;
- if (mName != null ? !mName.equals(that.mName) : that.mName != null) return false;
- return mRingtone != null ? mRingtone.equals(
- that.mRingtone) : that.mRingtone == null;
+ if (getUserLockedFields() != that.getUserLockedFields()) return false;
+ if (getId() != null ? !getId().equals(that.getId()) : that.getId() != null) return false;
+ if (getName() != null ? !getName().equals(that.getName()) : that.getName() != null)
+ return false;
+ return getRingtone() != null ? getRingtone().equals(
+ that.getRingtone()) : that.getRingtone() == null;
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = getId() != null ? getId().hashCode() : 0;
+ result = 31 * result + (getName() != null ? getName().hashCode() : 0);
+ result = 31 * result + getImportance();
+ result = 31 * result + (mBypassDnd ? 1 : 0);
+ result = 31 * result + getLockscreenVisibility();
+ result = 31 * result + (getRingtone() != null ? getRingtone().hashCode() : 0);
+ result = 31 * result + (mLights ? 1 : 0);
+ result = 31 * result + (mVibration ? 1 : 0);
+ result = 31 * result + getUserLockedFields();
+ return result;
}
@Override
@@ -361,22 +459,10 @@
", mImportance=" + mImportance +
", mBypassDnd=" + mBypassDnd +
", mLockscreenVisibility=" + mLockscreenVisibility +
- ", mRingtone='" + mRingtone + '\'' +
+ ", mRingtone=" + mRingtone +
", mLights=" + mLights +
", mVibration=" + mVibration +
+ ", mUserLockedFields=" + mUserLockedFields +
'}';
}
-
- @Override
- public int hashCode() {
- int result = mId.hashCode();
- result = 31 * result + (mName != null ? mName.hashCode() : 0);
- result = 31 * result + mImportance;
- result = 31 * result + (mBypassDnd ? 1 : 0);
- result = 31 * result + mLockscreenVisibility;
- result = 31 * result + (mRingtone != null ? mRingtone.hashCode() : 0);
- result = 31 * result + (mLights ? 1 : 0);
- result = 31 * result + (mVibration ? 1 : 0);
- return result;
- }
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 39cd2b5..2234a38 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -181,15 +181,15 @@
public static final int INTERRUPTION_FILTER_UNKNOWN = 0;
/** @hide */
- @IntDef({VISIBILITY_NO_OVERRIDE, IMPORTANCE_UNSPECIFIED, IMPORTANCE_NONE,
- IMPORTANCE_MIN, IMPORTANCE_LOW, IMPORTANCE_DEFAULT, IMPORTANCE_HIGH,
- IMPORTANCE_MAX})
+ @IntDef({IMPORTANCE_UNSPECIFIED, IMPORTANCE_NONE,
+ IMPORTANCE_MIN, IMPORTANCE_LOW, IMPORTANCE_DEFAULT, IMPORTANCE_HIGH})
@Retention(RetentionPolicy.SOURCE)
public @interface Importance {}
/** Value signifying that the user has not expressed a per-app visibility override value.
* @hide */
public static final int VISIBILITY_NO_OVERRIDE = -1000;
+
/**
* Value signifying that the user has not expressed an importance.
*
@@ -214,19 +214,19 @@
public static final int IMPORTANCE_LOW = 2;
/**
- * Default notification importance: shows everywhere, allowed to makes noise,
- * but does not visually intrude.
+ * Default notification importance: shows everywhere, makes noise, but does not visually
+ * intrude.
*/
public static final int IMPORTANCE_DEFAULT = 3;
/**
- * Higher notification importance: shows everywhere, allowed to makes noise and peek.
+ * Higher notification importance: shows everywhere, makes noise and peeks. May use full screen
+ * intents.
*/
public static final int IMPORTANCE_HIGH = 4;
/**
- * Highest notification importance: shows everywhere, allowed to makes noise, peek, and
- * use full screen intents.
+ * Unused.
*/
public static final int IMPORTANCE_MAX = 5;
@@ -402,7 +402,7 @@
}
/**
- * Returns all notification channels created by the calling app.
+ * Returns all notification channels belonging to the calling app.
*/
public List<NotificationChannel> getNotificationChannels() {
INotificationManager service = getService();
@@ -414,18 +414,6 @@
}
/**
- * Updates settings for a given channel.
- */
- public void updateNotificationChannel(NotificationChannel channel) {
- INotificationManager service = getService();
- try {
- service.updateNotificationChannel(mContext.getPackageName(), channel);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Deletes the given notification channel.
*/
public void deleteNotificationChannel(String channelId) {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 542b06b..bb344a6 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1976,6 +1976,9 @@
} else if (profile == BluetoothProfile.PBAP_CLIENT) {
BluetoothPbapClient pbapClient = new BluetoothPbapClient(context, listener);
return true;
+ } else if (profile == BluetoothProfile.MAP_CLIENT) {
+ BluetoothMapClient mapClient = new BluetoothMapClient(context, listener);
+ return true;
} else {
return false;
}
@@ -2048,6 +2051,10 @@
BluetoothPbapClient pbapClient = (BluetoothPbapClient)proxy;
pbapClient.close();
break;
+ case BluetoothProfile.MAP_CLIENT:
+ BluetoothMapClient mapClient = (BluetoothMapClient)proxy;
+ mapClient.close();
+ break;
}
}
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
new file mode 100644
index 0000000..4252482
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -0,0 +1,415 @@
+/*
+ * 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.
+ */
+
+package android.bluetooth;
+
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class provides the APIs to control the Bluetooth MAP MCE Profile.
+ *
+ * @hide
+ */
+public final class BluetoothMapClient implements BluetoothProfile {
+
+ private static final String TAG = "BluetoothMapClient";
+ private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
+
+ public static final String ACTION_CONNECTION_STATE_CHANGED =
+ "android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED";
+ public static final String ACTION_MESSAGE_RECEIVED =
+ "android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED";
+ /* Actions to be used for pending intents */
+ public static final String ACTION_MESSAGE_SENT_SUCCESSFULLY =
+ "android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY";
+ public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY =
+ "android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY";
+
+ private IBluetoothMapClient mService;
+ private final Context mContext;
+ private ServiceListener mServiceListener;
+ private BluetoothAdapter mAdapter;
+
+ /** There was an error trying to obtain the state */
+ public static final int STATE_ERROR = -1;
+
+ public static final int RESULT_FAILURE = 0;
+ public static final int RESULT_SUCCESS = 1;
+ /** Connection canceled before completion. */
+ public static final int RESULT_CANCELED = 2;
+
+ final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
+ new IBluetoothStateChangeCallback.Stub() {
+ public void onBluetoothStateChange(boolean up) {
+ if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
+ if (!up) {
+ if (VDBG) Log.d(TAG, "Unbinding service...");
+ synchronized (mConnection) {
+ try {
+ mService = null;
+ mContext.unbindService(mConnection);
+ } catch (Exception re) {
+ Log.e(TAG, "", re);
+ }
+ }
+ } else {
+ synchronized (mConnection) {
+ try {
+ if (mService == null) {
+ if (VDBG) Log.d(TAG, "Binding service...");
+ doBind();
+ }
+ } catch (Exception re) {
+ Log.e(TAG, "", re);
+ }
+ }
+ }
+ }
+ };
+
+ /**
+ * Create a BluetoothMapClient proxy object.
+ */
+ /*package*/ BluetoothMapClient(Context context, ServiceListener l) {
+ if (DBG) Log.d(TAG, "Create BluetoothMapClient proxy object");
+ mContext = context;
+ mServiceListener = l;
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+ IBluetoothManager mgr = mAdapter.getBluetoothManager();
+ if (mgr != null) {
+ try {
+ mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+ doBind();
+ }
+
+ boolean doBind() {
+ Intent intent = new Intent(IBluetoothMapClient.class.getName());
+ ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+ intent.setComponent(comp);
+ if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
+ android.os.Process.myUserHandle())) {
+ Log.e(TAG, "Could not bind to Bluetooth MAP MCE Service with " + intent);
+ return false;
+ }
+ return true;
+ }
+
+ protected void finalize() throws Throwable {
+ try {
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /**
+ * Close the connection to the backing service.
+ * Other public functions of BluetoothMap will return default error
+ * results once close() has been called. Multiple invocations of close()
+ * are ok.
+ */
+ public void close() {
+ IBluetoothManager mgr = mAdapter.getBluetoothManager();
+ if (mgr != null) {
+ try {
+ mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (Exception e) {
+ Log.e(TAG, "", e);
+ }
+ }
+
+ synchronized (mConnection) {
+ if (mService != null) {
+ try {
+ mService = null;
+ mContext.unbindService(mConnection);
+ } catch (Exception re) {
+ Log.e(TAG, "", re);
+ }
+ }
+ }
+ mServiceListener = null;
+ }
+
+ /**
+ * Returns true if the specified Bluetooth device is connected.
+ * Returns false if not connected, or if this proxy object is not
+ * currently connected to the Map service.
+ */
+ public boolean isConnected(BluetoothDevice device) {
+ if (VDBG) Log.d(TAG, "isConnected(" + device + ")");
+ if (mService != null) {
+ try {
+ return mService.isConnected(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ return false;
+ }
+
+ /**
+ * Initiate connection. Initiation of outgoing connections is not
+ * supported for MAP server.
+ */
+ public boolean connect(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE");
+ if (mService != null) {
+ try {
+ return mService.connect(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ return false;
+ }
+
+ /**
+ * Initiate disconnect.
+ *
+ * @param device Remote Bluetooth Device
+ * @return false on error, true otherwise
+ */
+ public boolean disconnect(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "disconnect(" + device + ")");
+ if (mService != null && isEnabled() &&
+ isValidDevice(device)) {
+ try {
+ return mService.disconnect(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ }
+
+ /**
+ * Get the list of connected devices. Currently at most one.
+ *
+ * @return list of connected devices
+ */
+ @Override
+ public List<BluetoothDevice> getConnectedDevices() {
+ if (DBG) Log.d(TAG, "getConnectedDevices()");
+ if (mService != null && isEnabled()) {
+ try {
+ return mService.getConnectedDevices();
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return new ArrayList<>();
+ }
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return new ArrayList<>();
+ }
+
+ /**
+ * Get the list of devices matching specified states. Currently at most one.
+ *
+ * @return list of matching devices
+ */
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+ if (DBG) Log.d(TAG, "getDevicesMatchingStates()");
+ if (mService != null && isEnabled()) {
+ try {
+ return mService.getDevicesMatchingConnectionStates(states);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return new ArrayList<>();
+ }
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return new ArrayList<>();
+ }
+
+ /**
+ * Get connection state of device
+ *
+ * @return device connection state
+ */
+ @Override
+ public int getConnectionState(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "getConnectionState(" + device + ")");
+ if (mService != null && isEnabled() &&
+ isValidDevice(device)) {
+ try {
+ return mService.getConnectionState(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+
+ /**
+ * Set priority of the profile
+ *
+ * <p> The device should already be paired. Priority can be one of {@link #PRIORITY_ON} or
+ * {@link #PRIORITY_OFF},
+ *
+ * @param device Paired bluetooth device
+ * @return true if priority is set, false on error
+ */
+ public boolean setPriority(BluetoothDevice device, int priority) {
+ if (DBG) Log.d(TAG, "setPriority(" + device + ", " + priority + ")");
+ if (mService != null && isEnabled() &&
+ isValidDevice(device)) {
+ if (priority != BluetoothProfile.PRIORITY_OFF &&
+ priority != BluetoothProfile.PRIORITY_ON) {
+ return false;
+ }
+ try {
+ return mService.setPriority(device, priority);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ }
+
+ /**
+ * Get the priority of the profile.
+ *
+ * <p> The priority can be any of:
+ * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
+ * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
+ *
+ * @param device Bluetooth device
+ * @return priority of the device
+ */
+ public int getPriority(BluetoothDevice device) {
+ if (VDBG) Log.d(TAG, "getPriority(" + device + ")");
+ if (mService != null && isEnabled() &&
+ isValidDevice(device)) {
+ try {
+ return mService.getPriority(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return PRIORITY_OFF;
+ }
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return PRIORITY_OFF;
+ }
+
+ /**
+ * Send a message.
+ *
+ * Send an SMS message to either the contacts primary number or the telephone number specified.
+ *
+ * @param device Bluetooth device
+ * @param contacts Uri[] of the contacts
+ * @param message Message to be sent
+ * @param sentIntent intent issued when message is sent
+ * @param deliveredIntent intent issued when message is delivered
+ * @return true if the message is enqueued, false on error
+ */
+ public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message,
+ PendingIntent sentIntent, PendingIntent deliveredIntent) {
+ if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message);
+ if (mService != null && isEnabled() && isValidDevice(device)) {
+ try {
+ return mService.sendMessage(device, contacts, message, sentIntent, deliveredIntent);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get unread messages. Unread messages will be published via {@link #ACTION_MESSAGE_RECEIVED}.
+ *
+ * @param device Bluetooth device
+ * @return true if the message is enqueued, false on error
+ */
+ public boolean getUnreadMessages(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")");
+ if (mService != null && isEnabled() && isValidDevice(device)) {
+ try {
+ return mService.getUnreadMessages(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ }
+ return false;
+ }
+
+ private final ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ if (DBG) Log.d(TAG, "Proxy object connected");
+ mService = IBluetoothMapClient.Stub.asInterface(service);
+ if (mServiceListener != null) {
+ mServiceListener.onServiceConnected(BluetoothProfile.MAP_CLIENT,
+ BluetoothMapClient.this);
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ if (DBG) Log.d(TAG, "Proxy object disconnected");
+ mService = null;
+ if (mServiceListener != null) {
+ mServiceListener.onServiceDisconnected(BluetoothProfile.MAP_CLIENT);
+ }
+ }
+ };
+
+ private boolean isEnabled() {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true;
+ if (DBG) Log.d(TAG, "Bluetooth is Not enabled");
+ return false;
+ }
+
+ private boolean isValidDevice(BluetoothDevice device) {
+ if (device == null) return false;
+
+ if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
+ return false;
+ }
+
+
+}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 20d95cc..f363607 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2014 The Android Open Source Project
+ * Copyright (C) 2010-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.
@@ -137,6 +137,12 @@
public static final int PBAP_CLIENT = 17;
/**
+ * MAP Messaging Client Equipment (MCE)
+ * @hide
+ */
+ public static final int MAP_CLIENT = 18;
+
+ /**
* Max profile ID. This value should be updated whenever a new profile is added to match
* the largest value assigned to a profile.
* @hide
diff --git a/core/java/android/bluetooth/IBluetoothMapClient.aidl b/core/java/android/bluetooth/IBluetoothMapClient.aidl
new file mode 100644
index 0000000..df45af9
--- /dev/null
+++ b/core/java/android/bluetooth/IBluetoothMapClient.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package android.bluetooth;
+
+import android.app.PendingIntent;
+import android.bluetooth.BluetoothDevice;
+import android.net.Uri;
+
+/**
+ * System private API for Bluetooth MAP MCE service
+ *
+ * {@hide}
+ */
+interface IBluetoothMapClient {
+ boolean connect(in BluetoothDevice device);
+ boolean disconnect(in BluetoothDevice device);
+ boolean isConnected(in BluetoothDevice device);
+ List<BluetoothDevice> getConnectedDevices();
+ List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
+ int getConnectionState(in BluetoothDevice device);
+ boolean setPriority(in BluetoothDevice device,in int priority);
+ int getPriority(in BluetoothDevice device);
+ boolean sendMessage(in BluetoothDevice device, in Uri[] contacts, in String message,
+ in PendingIntent sentIntent, in PendingIntent deliveryIntent);
+ boolean getUnreadMessages(in BluetoothDevice device);
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 68b7b3a..89c6eb2 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8289,6 +8289,9 @@
BLUETOOTH_MAP_PRIORITY_PREFIX = "bluetooth_map_priority_";
/** {@hide} */
public static final String
+ BLUETOOTH_MAP_CLIENT_PRIORITY_PREFIX = "bluetooth_map_client_priority_";
+ /** {@hide} */
+ public static final String
BLUETOOTH_PBAP_CLIENT_PRIORITY_PREFIX = "bluetooth_pbap_client_priority_";
/** {@hide} */
public static final String
@@ -8489,6 +8492,14 @@
}
/**
+ * Get the key that retrieves a bluetooth map client priority.
+ * @hide
+ */
+ public static final String getBluetoothMapClientPriorityKey(String address) {
+ return BLUETOOTH_MAP_CLIENT_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
+ }
+
+ /**
* Get the key that retrieves a bluetooth pbap client priority.
* @hide
*/
@@ -8497,7 +8508,7 @@
}
/**
- * Get the key that retrieves a bluetooth map priority.
+ * Get the key that retrieves a bluetooth sap priority.
* @hide
*/
public static final String getBluetoothSapPriorityKey(String address) {
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index e6f58f5..e606ebf 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1037,60 +1037,6 @@
* @hide */
public static final int VISIBILITY_NO_OVERRIDE = NotificationManager.VISIBILITY_NO_OVERRIDE;
- /**
- * Value signifying that the user has not expressed an importance.
- *
- * This value is for persisting preferences, and should never be associated with
- * an actual notification.
- *
- * @hide
- */
- public static final int IMPORTANCE_UNSPECIFIED = NotificationManager.IMPORTANCE_UNSPECIFIED;
-
- /**
- * A notification with no importance: shows nowhere, is blocked.
- *
- * @hide
- */
- public static final int IMPORTANCE_NONE = NotificationManager.IMPORTANCE_NONE;
-
- /**
- * Min notification importance: only shows in the shade, below the fold.
- *
- * @hide
- */
- public static final int IMPORTANCE_MIN = NotificationManager.IMPORTANCE_MIN;
-
- /**
- * Low notification importance: shows everywhere, but is not intrusive.
- *
- * @hide
- */
- public static final int IMPORTANCE_LOW = NotificationManager.IMPORTANCE_LOW;
-
- /**
- * Default notification importance: shows everywhere, allowed to makes noise,
- * but does not visually intrude.
- *
- * @hide
- */
- public static final int IMPORTANCE_DEFAULT = NotificationManager.IMPORTANCE_DEFAULT;
-
- /**
- * Higher notification importance: shows everywhere, allowed to makes noise and peek.
- *
- * @hide
- */
- public static final int IMPORTANCE_HIGH = NotificationManager.IMPORTANCE_HIGH;
-
- /**
- * Highest notification importance: shows everywhere, allowed to makes noise, peek, and
- * use full screen intents.
- *
- * @hide
- */
- public static final int IMPORTANCE_MAX = NotificationManager.IMPORTANCE_MAX;
-
private String mKey;
private int mRank = -1;
private boolean mIsAmbient;
@@ -1192,7 +1138,7 @@
CharSequence explanation, String overrideGroupKey) {
mKey = key;
mRank = rank;
- mIsAmbient = importance < IMPORTANCE_LOW;
+ mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
mMatchesInterruptionFilter = matchesInterruptionFilter;
mVisibilityOverride = visibilityOverride;
mSuppressedVisualEffects = suppressedVisualEffects;
@@ -1206,20 +1152,19 @@
*/
public static String importanceToString(int importance) {
switch (importance) {
- case IMPORTANCE_UNSPECIFIED:
+ case NotificationManager.IMPORTANCE_UNSPECIFIED:
return "UNSPECIFIED";
- case IMPORTANCE_NONE:
+ case NotificationManager.IMPORTANCE_NONE:
return "NONE";
- case IMPORTANCE_MIN:
+ case NotificationManager.IMPORTANCE_MIN:
return "MIN";
- case IMPORTANCE_LOW:
+ case NotificationManager.IMPORTANCE_LOW:
return "LOW";
- case IMPORTANCE_DEFAULT:
+ case NotificationManager.IMPORTANCE_DEFAULT:
return "DEFAULT";
- case IMPORTANCE_HIGH:
+ case NotificationManager.IMPORTANCE_HIGH:
+ case NotificationManager.IMPORTANCE_MAX:
return "HIGH";
- case IMPORTANCE_MAX:
- return "MAX";
default:
return "UNKNOWN(" + String.valueOf(importance) + ")";
}
@@ -1326,7 +1271,7 @@
}
Integer importance = mImportance.get(key);
if (importance == null) {
- return Ranking.IMPORTANCE_DEFAULT;
+ return NotificationManager.IMPORTANCE_DEFAULT;
}
return importance.intValue();
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 20876a9..ba26926 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7194,7 +7194,6 @@
/**
* Gets the unique identifier of this view on the screen for accessibility purposes.
- * If this {@link View} is not attached to any window, {@value #NO_ID} is returned.
*
* @return The view accessibility id.
*
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 87b330d..5a6cf7d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -19,7 +19,6 @@
import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER;
import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
@@ -3542,20 +3541,7 @@
mWinFrame.bottom = t + h;
mPendingBackDropFrame.set(mWinFrame);
-
- // Suppress layouts during resizing - a correct layout will happen when resizing
- // is done, and this just increases system load.
- boolean isDockedDivider = mWindowAttributes.type == TYPE_DOCK_DIVIDER;
- boolean suppress = (mDragResizing && mResizeMode == RESIZE_MODE_DOCKED_DIVIDER)
- || isDockedDivider;
- if (!suppress) {
- if (mView != null) {
- forceLayout(mView);
- }
- requestLayout();
- } else {
- maybeHandleWindowMove(mWinFrame);
- }
+ maybeHandleWindowMove(mWinFrame);
}
break;
case MSG_WINDOW_FOCUS_CHANGED: {
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 567e920..0678dfd 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2635,4 +2635,11 @@
<!-- An array of packages for which notifications cannot be blocked. -->
<string-array translatable="false" name="config_nonBlockableNotificationPackages" />
+
+ <!-- The default value for transition animation scale found in developer settings.
+ 1.0 corresponds to 1x animator scale, 0 means that there will be no transition
+ animations. Note that this is only a default and will be overridden by a
+ user-set value if toggled by settings so the "Transition animation scale" setting
+ should also be hidden if intended to be permanent. -->
+ <item name="config_appTransitionAnimationDurationScaleDefault" format="float" type="dimen">1.0</item>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e946136..0073f53 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2731,4 +2731,6 @@
<!-- Screen-size-dependent modes for picker dialogs. -->
<java-symbol type="integer" name="time_picker_mode" />
<java-symbol type="integer" name="date_picker_mode" />
+
+ <java-symbol type="dimen" name="config_appTransitionAnimationDurationScaleDefault" />
</resources>
diff --git a/packages/ExtServices/src/android/ext/services/notification/Ranker.java b/packages/ExtServices/src/android/ext/services/notification/Ranker.java
index 63fc157..2feb51f 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Ranker.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Ranker.java
@@ -16,8 +16,6 @@
package android.ext.services.notification;
-import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
-
import android.os.Bundle;
import android.os.UserHandle;
import android.service.notification.Adjustment;
@@ -36,6 +34,7 @@
/**
* Class that provides an updatable ranker module for the notification manager.
+ * TODO: delete
*/
public final class Ranker extends NotificationRankerService {
private static final String TAG = "RocketRanker";
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 7f0e27a..1ea592d 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -22,6 +22,7 @@
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHeadsetClient;
import android.bluetooth.BluetoothMap;
+import android.bluetooth.BluetoothMapClient;
import android.bluetooth.BluetoothInputDevice;
import android.bluetooth.BluetoothPan;
import android.bluetooth.BluetoothPbapClient;
@@ -81,12 +82,14 @@
private HeadsetProfile mHeadsetProfile;
private HfpClientProfile mHfpClientProfile;
private MapProfile mMapProfile;
+ private MapClientProfile mMapClientProfile;
private final HidProfile mHidProfile;
private OppProfile mOppProfile;
private final PanProfile mPanProfile;
private PbapClientProfile mPbapClientProfile;
private final PbapServerProfile mPbapProfile;
private final boolean mUsePbapPce;
+ private final boolean mUseMapClient;
/**
* Mapping from profile name, e.g. "HEADSET" to profile object.
@@ -104,6 +107,8 @@
mDeviceManager = deviceManager;
mEventManager = eventManager;
mUsePbapPce = mContext.getResources().getBoolean(R.bool.enable_pbap_pce_profile);
+ // MAP Client is typically used in the same situations as PBAP Client
+ mUseMapClient = mContext.getResources().getBoolean(R.bool.enable_pbap_pce_profile);
// pass this reference to adapter and event manager (circular dependency)
mLocalAdapter.setProfileManager(this);
mEventManager.setProfileManager(this);
@@ -125,10 +130,15 @@
BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
if(DEBUG) Log.d(TAG, "Adding local MAP profile");
- mMapProfile = new MapProfile(mContext, mLocalAdapter,
- mDeviceManager, this);
- addProfile(mMapProfile, MapProfile.NAME,
- BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
+ if (mUseMapClient) {
+ mMapClientProfile = new MapClientProfile(mContext, mLocalAdapter, mDeviceManager, this);
+ addProfile(mMapClientProfile, MapClientProfile.NAME,
+ BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
+ } else {
+ mMapProfile = new MapProfile(mContext, mLocalAdapter, mDeviceManager, this);
+ addProfile(mMapProfile, MapProfile.NAME,
+ BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
+ }
//Create PBAP server profile, but do not add it to list of profiles
// as we do not need to monitor the profile as part of profile list
@@ -199,6 +209,22 @@
Log.d(TAG, "Handsfree Uuid not found.");
}
+ // Message Access Profile Client
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.MNS)) {
+ if (mMapClientProfile == null) {
+ if(DEBUG) Log.d(TAG, "Adding local Map Client profile");
+ mMapClientProfile =
+ new MapClientProfile(mContext, mLocalAdapter, mDeviceManager, this);
+ addProfile(mMapClientProfile, MapClientProfile.NAME,
+ BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
+ }
+ } else if (mMapClientProfile != null) {
+ Log.w(TAG,
+ "Warning: MAP Client profile was previously added but the UUID is now missing.");
+ } else {
+ Log.d(TAG, "MAP Client Uuid not found.");
+ }
+
// OPP
if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush)) {
if (mOppProfile == null) {
@@ -383,6 +409,10 @@
return mMapProfile;
}
+ public MapClientProfile getMapClientProfile() {
+ return mMapClientProfile;
+ }
+
/**
* Fill in a list of LocalBluetoothProfile objects that are supported by
* the local device and the remote device.
@@ -465,6 +495,11 @@
mMapProfile.setPreferred(device, true);
}
+ if (mMapClientProfile != null) {
+ profiles.add(mMapClientProfile);
+ removedProfiles.remove(mMapClientProfile);
+ }
+
if (mUsePbapPce) {
profiles.add(mPbapClientProfile);
removedProfiles.remove(mPbapClientProfile);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
new file mode 100644
index 0000000..a7621fc
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothMapClient;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.Context;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * MapClientProfile handles Bluetooth MAP profile.
+ */
+public final class MapClientProfile implements LocalBluetoothProfile {
+ private static final String TAG = "MapClientProfile";
+ private static boolean V = false;
+
+ private BluetoothMapClient mService;
+ private boolean mIsProfileReady;
+
+ private final LocalBluetoothAdapter mLocalAdapter;
+ private final CachedBluetoothDeviceManager mDeviceManager;
+ private final LocalBluetoothProfileManager mProfileManager;
+
+ static final ParcelUuid[] UUIDS = {
+ BluetoothUuid.MAP,
+ BluetoothUuid.MNS,
+ BluetoothUuid.MAS,
+ };
+
+ static final String NAME = "MAP Client";
+
+ // Order of this profile in device profiles list
+ private static final int ORDINAL = 0;
+
+ // These callbacks run on the main thread.
+ private final class MapClientServiceListener
+ implements BluetoothProfile.ServiceListener {
+
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (V) Log.d(TAG,"Bluetooth service connected");
+ mService = (BluetoothMapClient) proxy;
+ // We just bound to the service, so refresh the UI for any connected MAP devices.
+ List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+ while (!deviceList.isEmpty()) {
+ BluetoothDevice nextDevice = deviceList.remove(0);
+ CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
+ // we may add a new device here, but generally this should not happen
+ if (device == null) {
+ Log.w(TAG, "MapProfile found new device: " + nextDevice);
+ device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+ }
+ device.onProfileStateChanged(MapClientProfile.this,
+ BluetoothProfile.STATE_CONNECTED);
+ device.refresh();
+ }
+
+ mProfileManager.callServiceConnectedListeners();
+ mIsProfileReady=true;
+ }
+
+ public void onServiceDisconnected(int profile) {
+ if (V) Log.d(TAG,"Bluetooth service disconnected");
+ mProfileManager.callServiceDisconnectedListeners();
+ mIsProfileReady=false;
+ }
+ }
+
+ public boolean isProfileReady() {
+ if(V) Log.d(TAG,"isProfileReady(): "+ mIsProfileReady);
+ return mIsProfileReady;
+ }
+
+ MapClientProfile(Context context, LocalBluetoothAdapter adapter,
+ CachedBluetoothDeviceManager deviceManager,
+ LocalBluetoothProfileManager profileManager) {
+ mLocalAdapter = adapter;
+ mDeviceManager = deviceManager;
+ mProfileManager = profileManager;
+ mLocalAdapter.getProfileProxy(context, new MapClientServiceListener(),
+ BluetoothProfile.MAP_CLIENT);
+ }
+
+ public boolean isConnectable() {
+ return true;
+ }
+
+ public boolean isAutoConnectable() {
+ return true;
+ }
+
+ public boolean connect(BluetoothDevice device) {
+ if (mService == null) return false;
+ List<BluetoothDevice> connectedDevices = getConnectedDevices();
+ if (connectedDevices != null) {
+ for (BluetoothDevice connectedDevice : connectedDevices) {
+ mService.disconnect(connectedDevice);
+ }
+ }
+ return mService.connect(device);
+ }
+
+ public boolean disconnect(BluetoothDevice device) {
+ if (mService == null) return false;
+ // Downgrade priority as user is disconnecting.
+ if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ return mService.disconnect(device);
+ }
+
+ public int getConnectionStatus(BluetoothDevice device) {
+ if (mService == null) return BluetoothProfile.STATE_DISCONNECTED;
+
+ return mService.getConnectionState(device);
+ }
+
+ public boolean isPreferred(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+ }
+
+ public int getPreferred(BluetoothDevice device) {
+ if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+ return mService.getPriority(device);
+ }
+
+ public void setPreferred(BluetoothDevice device, boolean preferred) {
+ if (mService == null) return;
+ if (preferred) {
+ if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ } else {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+ }
+ }
+
+ public List<BluetoothDevice> getConnectedDevices() {
+ if (mService == null) return new ArrayList<BluetoothDevice>(0);
+ return mService.getDevicesMatchingConnectionStates(
+ new int[] {BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTING});
+ }
+
+ public String toString() {
+ return NAME;
+ }
+
+ public int getOrdinal() {
+ return ORDINAL;
+ }
+
+ public int getNameResource(BluetoothDevice device) {
+ return R.string.bluetooth_profile_map;
+ }
+
+ public int getSummaryResourceForDevice(BluetoothDevice device) {
+ int state = getConnectionStatus(device);
+ switch (state) {
+ case BluetoothProfile.STATE_DISCONNECTED:
+ return R.string.bluetooth_map_profile_summary_use_for;
+
+ case BluetoothProfile.STATE_CONNECTED:
+ return R.string.bluetooth_map_profile_summary_connected;
+
+ default:
+ return Utils.getConnectionStateSummary(state);
+ }
+ }
+
+ public int getDrawableResource(BluetoothClass btClass) {
+ return R.drawable.ic_bt_cellphone;
+ }
+
+ protected void finalize() {
+ if (V) Log.d(TAG, "finalize()");
+ if (mService != null) {
+ try {
+ BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.MAP_CLIENT,
+ mService);
+ mService = null;
+ }catch (Throwable t) {
+ Log.w(TAG, "Error cleaning up MAP Client proxy", t);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index d7c9eab..72a3b3a 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -145,7 +145,7 @@
}
}
for (BluetoothDevice src : srcs) {
- mService.disconnect(device);
+ mService.disconnect(src);
}
}
Log.d(TAG,"PBAPClientProfile attempting to connect to " + device.getAddress());
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index fd1ac8e..81dfafc 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -30,13 +30,10 @@
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
-import android.media.AudioAttributes;
import android.net.Uri;
import android.os.Handler;
import android.os.PowerManager;
-import android.os.SystemClock;
import android.os.UserHandle;
-import android.os.Vibrator;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
@@ -104,7 +101,7 @@
return null;
}
- public void setListen(boolean listen) {
+ public void setListening(boolean listen) {
for (TriggerSensor s : mSensors) {
s.setListening(listen);
if (listen) {
@@ -209,8 +206,7 @@
@AnyThread
public void onTrigger(TriggerEvent event) {
mHandler.post(mWakeLock.wrap(() -> {
- if (DEBUG)
- Log.d(TAG, "onTrigger: " + triggerEventToString(event));
+ if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event));
boolean sensorPerformsProxCheck = false;
if (mSensor.getType() == Sensor.TYPE_PICK_UP_GESTURE) {
int subType = (int) event.values[0];
@@ -250,6 +246,12 @@
}
public interface Callback {
+
+ /**
+ * Called when a sensor requests a pulse
+ * @param pulseReason Requesting sensor, e.g. {@link DozeLog#PULSE_REASON_SENSOR_PICKUP}
+ * @param sensorPerformedProxCheck true if the sensor already checked for FAR proximity.
+ */
void onSensorPulse(int pulseReason, boolean sensorPerformedProxCheck);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index cbaf232..2bb1d6a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -36,8 +36,6 @@
import android.view.Display;
import com.android.internal.hardware.AmbientDisplayConfiguration;
-import com.android.internal.util.Preconditions;
-import com.android.systemui.DejankUtils;
import com.android.systemui.SystemUIApplication;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.Assert;
@@ -298,7 +296,7 @@
private void listenForPulseSignals(boolean listen) {
if (DEBUG) Log.d(mTag, "listenForPulseSignals: " + listen);
- mDozeSensors.setListen(listen);
+ mDozeSensors.setListening(listen);
listenForBroadcasts(listen);
listenForNotifications(listen);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index f2f7993..70e21d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -119,8 +119,6 @@
import java.util.Locale;
import java.util.Set;
-import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
-
public abstract class BaseStatusBar extends SystemUI implements
CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
@@ -2540,7 +2538,7 @@
return false;
}
- if (mNotificationData.getImportance(sbn.getKey()) < IMPORTANCE_HIGH) {
+ if (mNotificationData.getImportance(sbn.getKey()) < NotificationManager.IMPORTANCE_HIGH) {
if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index bae16f3..7019880 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar;
import android.app.Notification;
+import android.app.NotificationManager;
import android.content.Context;
import android.os.SystemClock;
import android.service.notification.NotificationListenerService;
@@ -186,8 +187,8 @@
public int compare(Entry a, Entry b) {
final StatusBarNotification na = a.notification;
final StatusBarNotification nb = b.notification;
- int aImportance = Ranking.IMPORTANCE_DEFAULT;
- int bImportance = Ranking.IMPORTANCE_DEFAULT;
+ int aImportance = NotificationManager.IMPORTANCE_DEFAULT;
+ int bImportance = NotificationManager.IMPORTANCE_DEFAULT;
int aRank = 0;
int bRank = 0;
@@ -205,13 +206,13 @@
// IMPORTANCE_MIN media streams are allowed to drift to the bottom
final boolean aMedia = a.key.equals(mediaNotification)
- && aImportance > Ranking.IMPORTANCE_MIN;
+ && aImportance > NotificationManager.IMPORTANCE_MIN;
final boolean bMedia = b.key.equals(mediaNotification)
- && bImportance > Ranking.IMPORTANCE_MIN;
+ && bImportance > NotificationManager.IMPORTANCE_MIN;
- boolean aSystemMax = aImportance >= Ranking.IMPORTANCE_MAX &&
+ boolean aSystemMax = aImportance >= NotificationManager.IMPORTANCE_HIGH &&
isSystemNotification(na);
- boolean bSystemMax = bImportance >= Ranking.IMPORTANCE_MAX &&
+ boolean bSystemMax = bImportance >= NotificationManager.IMPORTANCE_HIGH &&
isSystemNotification(nb);
boolean isHeadsUp = a.row.isHeadsUp();
@@ -318,7 +319,7 @@
mRankingMap.getRanking(key, mTmpRanking);
return mTmpRanking.getImportance();
}
- return Ranking.IMPORTANCE_UNSPECIFIED;
+ return NotificationManager.IMPORTANCE_UNSPECIFIED;
}
public String getOverrideGroupKey(String key) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 62d730a..b10fb31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -19,6 +19,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.INotificationManager;
+import android.app.NotificationManager;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -30,7 +31,6 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
import android.view.View;
@@ -178,7 +178,7 @@
final Set<String> nonBlockablePkgs, final int importance) {
mINotificationManager = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
- mStartingUserImportance = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
+ mStartingUserImportance = NotificationManager.IMPORTANCE_UNSPECIFIED;
try {
mStartingUserImportance =
mINotificationManager.getImportance(sbn.getPackageName(), sbn.getUid());
@@ -229,15 +229,15 @@
if (mSeekBar.isEnabled()) {
return mSeekBar.getProgress();
} else {
- return Ranking.IMPORTANCE_UNSPECIFIED;
+ return NotificationManager.IMPORTANCE_UNSPECIFIED;
}
} else {
if (mBlock.isChecked()) {
- return Ranking.IMPORTANCE_NONE;
+ return NotificationManager.IMPORTANCE_NONE;
} else if (mSilent.isChecked()) {
- return Ranking.IMPORTANCE_LOW;
+ return NotificationManager.IMPORTANCE_LOW;
} else {
- return Ranking.IMPORTANCE_UNSPECIFIED;
+ return NotificationManager.IMPORTANCE_UNSPECIFIED;
}
}
}
@@ -262,7 +262,7 @@
}
mBlock.setText(mContext.getString(R.string.block));
mSilent.setText(mContext.getString(R.string.show_silently));
- if (importance == NotificationListenerService.Ranking.IMPORTANCE_LOW) {
+ if (importance == NotificationManager.IMPORTANCE_LOW) {
mSilent.setChecked(true);
} else {
mReset.setChecked(true);
@@ -278,9 +278,9 @@
mSeekBar = (SeekBar) importanceSlider.findViewById(R.id.seekbar);
final int minProgress = nonBlockable ?
- NotificationListenerService.Ranking.IMPORTANCE_MIN
- : NotificationListenerService.Ranking.IMPORTANCE_NONE;
- mSeekBar.setMax(NotificationListenerService.Ranking.IMPORTANCE_MAX);
+ NotificationManager.IMPORTANCE_MIN
+ : NotificationManager.IMPORTANCE_NONE;
+ mSeekBar.setMax(NotificationManager.IMPORTANCE_HIGH);
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
@@ -317,7 +317,7 @@
applyAuto();
}
});
- mAuto = mStartingUserImportance == Ranking.IMPORTANCE_UNSPECIFIED;
+ mAuto = mStartingUserImportance == NotificationManager.IMPORTANCE_UNSPECIFIED;
applyAuto();
}
@@ -344,36 +344,32 @@
private void updateTitleAndSummary(int progress) {
switch (progress) {
- case Ranking.IMPORTANCE_NONE:
+ case NotificationManager.IMPORTANCE_NONE:
mImportanceSummary.setText(mContext.getString(
R.string.notification_importance_blocked));
mImportanceTitle.setText(mContext.getString(R.string.blocked_importance));
break;
- case Ranking.IMPORTANCE_MIN:
+ case NotificationManager.IMPORTANCE_MIN:
mImportanceSummary.setText(mContext.getString(
R.string.notification_importance_min));
mImportanceTitle.setText(mContext.getString(R.string.min_importance));
break;
- case Ranking.IMPORTANCE_LOW:
+ case NotificationManager.IMPORTANCE_LOW:
mImportanceSummary.setText(mContext.getString(
R.string.notification_importance_low));
mImportanceTitle.setText(mContext.getString(R.string.low_importance));
break;
- case Ranking.IMPORTANCE_DEFAULT:
+ case NotificationManager.IMPORTANCE_DEFAULT:
mImportanceSummary.setText(mContext.getString(
R.string.notification_importance_default));
mImportanceTitle.setText(mContext.getString(R.string.default_importance));
break;
- case Ranking.IMPORTANCE_HIGH:
+ case NotificationManager.IMPORTANCE_HIGH:
+ case NotificationManager.IMPORTANCE_MAX:
mImportanceSummary.setText(mContext.getString(
R.string.notification_importance_high));
mImportanceTitle.setText(mContext.getString(R.string.high_importance));
break;
- case Ranking.IMPORTANCE_MAX:
- mImportanceSummary.setText(mContext.getString(
- R.string.notification_importance_max));
- mImportanceTitle.setText(mContext.getString(R.string.max_importance));
- break;
}
}
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 213fba7..669a512 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -38,6 +38,7 @@
import android.app.ActivityOptions;
import android.app.IActivityManager;
import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
@@ -1560,7 +1561,7 @@
Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + notification.getKey());
}
} else if (mNotificationData.getImportance(notification.getKey())
- < NotificationListenerService.Ranking.IMPORTANCE_MAX) {
+ < NotificationManager.IMPORTANCE_HIGH) {
if (DEBUG) {
Log.d(TAG, "No Fullscreen intent: not important enough: "
+ notification.getKey());
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 1484420..8234f35 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -141,7 +141,8 @@
case "broadcast":
return runSendBroadcast(pw);
case "instrument":
- return runInstrument(pw);
+ getOutPrintWriter().println("Error: must be invoked through 'am instrument'.");
+ return -1;
case "trace-ipc":
return runTraceIpc(pw);
case "profile":
@@ -561,224 +562,6 @@
return 0;
}
- final static class InstrumentationWatcher extends IInstrumentationWatcher.Stub {
- private final IActivityManager mInterface;
- private final PrintWriter mPw;
- private boolean mFinished = false;
- private boolean mRawMode = false;
-
- InstrumentationWatcher(IActivityManager iam, PrintWriter pw) {
- mInterface = iam;
- mPw = pw;
- }
-
- /**
- * Set or reset "raw mode". In "raw mode", all bundles are dumped. In "pretty mode",
- * if a bundle includes Instrumentation.REPORT_KEY_STREAMRESULT, just print that.
- * @param rawMode true for raw mode, false for pretty mode.
- */
- public void setRawOutput(boolean rawMode) {
- mRawMode = rawMode;
- }
-
- @Override
- public void instrumentationStatus(ComponentName name, int resultCode, Bundle results) {
- synchronized (this) {
- // pretty printer mode?
- String pretty = null;
- if (!mRawMode && results != null) {
- pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
- }
- if (pretty != null) {
- mPw.print(pretty);
- } else {
- if (results != null) {
- for (String key : results.keySet()) {
- mPw.println(
- "INSTRUMENTATION_STATUS: " + key + "=" + results.get(key));
- }
- }
- mPw.println("INSTRUMENTATION_STATUS_CODE: " + resultCode);
- }
- mPw.flush();
- notifyAll();
- }
- }
-
- @Override
- public void instrumentationFinished(ComponentName name, int resultCode,
- Bundle results) {
- synchronized (this) {
- // pretty printer mode?
- String pretty = null;
- if (!mRawMode && results != null) {
- pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
- }
- if (pretty != null) {
- mPw.println(pretty);
- } else {
- if (results != null) {
- for (String key : results.keySet()) {
- mPw.println(
- "INSTRUMENTATION_RESULT: " + key + "=" + results.get(key));
- }
- }
- mPw.println("INSTRUMENTATION_CODE: " + resultCode);
- }
- mPw.flush();
- mFinished = true;
- notifyAll();
- }
- }
-
- public boolean waitForFinish() {
- synchronized (this) {
- while (!mFinished) {
- try {
- if (!mInterface.asBinder().pingBinder()) {
- return false;
- }
- wait(1000);
- } catch (InterruptedException e) {
- throw new IllegalStateException(e);
- }
- }
- }
- return true;
- }
- }
-
- int runInstrument(PrintWriter pw) throws RemoteException {
- final PrintWriter err = getErrPrintWriter();
- String profileFile = null;
- boolean wait = false;
- boolean rawMode = false;
- boolean no_window_animation = false;
- int userId = UserHandle.USER_CURRENT;
- Bundle args = new Bundle();
- String argKey = null, argValue = null;
- IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
- String abi = null;
-
- String opt;
- while ((opt=getNextOption()) != null) {
- if (opt.equals("-p")) {
- profileFile = getNextArgRequired();
- } else if (opt.equals("-w")) {
- wait = true;
- } else if (opt.equals("-r")) {
- rawMode = true;
- } else if (opt.equals("-e")) {
- argKey = getNextArgRequired();
- argValue = getNextArgRequired();
- args.putString(argKey, argValue);
- } else if (opt.equals("--no_window_animation")
- || opt.equals("--no-window-animation")) {
- no_window_animation = true;
- } else if (opt.equals("--user")) {
- userId = UserHandle.parseUserArg(getNextArgRequired());
- } else if (opt.equals("--abi")) {
- abi = getNextArgRequired();
- } else {
- err.println("Error: Unknown option: " + opt);
- return -1;
- }
- }
-
- if (userId == UserHandle.USER_ALL) {
- err.println("Error: Can't start instrumentation with user 'all'");
- return -1;
- }
-
- String cnArg = getNextArgRequired();
-
- ComponentName cn;
- if (cnArg.contains("/")) {
- cn = ComponentName.unflattenFromString(cnArg);
- if (cn == null) throw new IllegalArgumentException("Bad component name: " + cnArg);
- } else {
- List<InstrumentationInfo> infos = mPm.queryInstrumentation(null, 0).getList();
-
- final int numInfos = infos == null ? 0: infos.size();
- List<ComponentName> cns = new ArrayList<>();
- for (int i = 0; i < numInfos; i++) {
- InstrumentationInfo info = infos.get(i);
-
- ComponentName c = new ComponentName(info.packageName, info.name);
- if (cnArg.equals(info.packageName)) {
- cns.add(c);
- }
- }
-
- if (cns.size() == 0) {
- throw new IllegalArgumentException("No instrumentation found for: " + cnArg);
- } else if (cns.size() == 1) {
- cn = cns.get(0);
- } else {
- StringBuilder cnsStr = new StringBuilder();
- final int numCns = cns.size();
- for (int i = 0; i < numCns; i++) {
- cnsStr.append(cns.get(i).flattenToString());
- cnsStr.append(", ");
- }
-
- // Remove last ", "
- cnsStr.setLength(cnsStr.length() - 2);
-
- throw new IllegalArgumentException("Found multiple instrumentations: "
- + cnsStr.toString());
- }
- }
-
- InstrumentationWatcher watcher = null;
- UiAutomationConnection connection = null;
- if (wait) {
- watcher = new InstrumentationWatcher(mInterface, pw);
- watcher.setRawOutput(rawMode);
- // Don't yet know how to support this.
- connection = null; //new UiAutomationConnection();
- }
-
- float[] oldAnims = null;
- if (no_window_animation) {
- oldAnims = wm.getAnimationScales();
- wm.setAnimationScale(0, 0.0f);
- wm.setAnimationScale(1, 0.0f);
- }
-
- if (abi != null) {
- final String[] supportedAbis = Build.SUPPORTED_ABIS;
- boolean matched = false;
- for (String supportedAbi : supportedAbis) {
- if (supportedAbi.equals(abi)) {
- matched = true;
- break;
- }
- }
-
- if (!matched) {
- throw new RuntimeException(
- "INSTRUMENTATION_FAILED: Unsupported instruction set " + abi);
- }
- }
-
- if (!mInterface.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId,
- abi)) {
- throw new RuntimeException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
- }
-
- if (watcher != null) {
- if (!watcher.waitForFinish()) {
- pw.println("INSTRUMENTATION_ABORTED: System has crashed.");
- }
- }
-
- if (oldAnims != null) {
- wm.setAnimationScales(oldAnims);
- }
- return 0;
- }
-
int runTraceIpc(PrintWriter pw) throws RemoteException {
String op = getNextArgRequired();
if (op.equals("start")) {
@@ -2550,6 +2333,25 @@
pw.println(" --user <USER_ID> | all | current: Specify which user to send to; if not");
pw.println(" specified then send to all users.");
pw.println(" --receiver-permission <PERMISSION>: Require receiver to hold permission.");
+ pw.println(" instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]");
+ pw.println(" [--user <USER_ID> | current]");
+ pw.println(" [--no-window-animation] [--abi <ABI>] <COMPONENT>");
+ pw.println(" Start an Instrumentation. Typically this target <COMPONENT> is in the");
+ pw.println(" form <TEST_PACKAGE>/<RUNNER_CLASS> or only <TEST_PACKAGE> if there");
+ pw.println(" is only one instrumentation. Options are:");
+ pw.println(" -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT). Use with");
+ pw.println(" [-e perf true] to generate raw output for performance measurements.");
+ pw.println(" -e <NAME> <VALUE>: set argument <NAME> to <VALUE>. For test runners a");
+ pw.println(" common form is [-e <testrunner_flag> <value>[,<value>...]].");
+ pw.println(" -p <FILE>: write profiling data to <FILE>");
+ pw.println(" -m: Write output as protobuf (machine readable)");
+ pw.println(" -w: wait for instrumentation to finish before returning. Required for");
+ pw.println(" test runners.");
+ pw.println(" --user <USER_ID> | current: Specify user instrumentation runs in;");
+ pw.println(" current user if not specified.");
+ pw.println(" --no-window-animation: turn off window animations while running.");
+ pw.println(" --abi <ABI>: Launch the instrumented process with the selected ABI.");
+ pw.println(" This assumes that the process supports the selected ABI.");
pw.println(" trace-ipc [start|stop] [--dump-file <FILE>]");
pw.println(" Trace IPC transactions.");
pw.println(" start: start tracing IPC transactions.");
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index cda063d..49c4140 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -46,7 +46,9 @@
import android.os.UserManager;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
+import com.android.server.FgThread;
import com.android.server.SystemService;
import org.json.JSONArray;
@@ -111,6 +113,7 @@
private Context mContext;
private long mHalDeviceId;
private int mFailedAttempts;
+ @GuardedBy("this")
private IFingerprintDaemon mDaemon;
private final PowerManager mPowerManager;
private final AlarmManager mAlarmManager;
@@ -195,35 +198,41 @@
public void binderDied() {
Slog.v(TAG, "fingerprintd died");
MetricsLogger.count(mContext, "fingerprintd_died", 1);
- mDaemon = null;
+ synchronized (this) {
+ mDaemon = null;
+ }
mCurrentUserId = UserHandle.USER_CURRENT;
handleError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
}
public IFingerprintDaemon getFingerprintDaemon() {
- if (mDaemon == null) {
- mDaemon = IFingerprintDaemon.Stub.asInterface(ServiceManager.getService(FINGERPRINTD));
- if (mDaemon != null) {
- try {
- mDaemon.asBinder().linkToDeath(this, 0);
- mDaemon.init(mDaemonCallback);
- mHalDeviceId = mDaemon.openHal();
- if (mHalDeviceId != 0) {
- updateActiveGroup(ActivityManager.getCurrentUser(), null);
- } else {
- Slog.w(TAG, "Failed to open Fingerprint HAL!");
- MetricsLogger.count(mContext, "fingerprintd_openhal_error", 1);
- mDaemon = null;
+ synchronized (this) {
+ if (mDaemon == null) {
+ mDaemon = IFingerprintDaemon.Stub
+ .asInterface(ServiceManager.getService(FINGERPRINTD));
+ if (mDaemon != null) {
+ try {
+ mDaemon.asBinder().linkToDeath(this, 0);
+ mDaemon.init(mDaemonCallback);
+ mHalDeviceId = mDaemon.openHal();
+ if (DEBUG) Slog.v(TAG, "Fingerprint HAL id: " + mHalDeviceId);
+ if (mHalDeviceId != 0) {
+ updateActiveGroup(ActivityManager.getCurrentUser(), null);
+ } else {
+ Slog.w(TAG, "Failed to open Fingerprint HAL!");
+ MetricsLogger.count(mContext, "fingerprintd_openhal_error", 1);
+ mDaemon = null;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to open fingeprintd HAL", e);
+ mDaemon = null; // try again later!
}
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to open fingeprintd HAL", e);
- mDaemon = null; // try again later!
+ } else {
+ Slog.w(TAG, "fingerprint service not available");
}
- } else {
- Slog.w(TAG, "fingerprint service not available");
}
+ return mDaemon;
}
- return mDaemon;
}
protected void handleEnumerate(long deviceId, int[] fingerIds, int[] groupIds) {
@@ -1005,8 +1014,7 @@
@Override
public void onStart() {
publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
- IFingerprintDaemon daemon = getFingerprintDaemon();
- if (DEBUG) Slog.v(TAG, "Fingerprint HAL id: " + mHalDeviceId);
+ FgThread.getHandler().post(() -> getFingerprintDaemon());
listenForUserSwitches();
}
diff --git a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
index bcdeb66..31ed350 100644
--- a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
@@ -17,6 +17,7 @@
package com.android.server.notification;
import android.app.Notification;
+import android.app.NotificationManager;
import android.content.Context;
import android.service.notification.NotificationListenerService;
import android.util.Log;
@@ -44,7 +45,7 @@
return null;
}
- if (record.getImportance() >= NotificationListenerService.Ranking.IMPORTANCE_DEFAULT) {
+ if (record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT) {
final Notification notification = record.getNotification();
if ((notification.defaults & Notification.DEFAULT_VIBRATE) != 0 ||
notification.vibrate != null ||
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4e50567..d782211 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -16,6 +16,8 @@
package com.android.server.notification;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.service.notification.NotificationRankerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationRankerService.REASON_APP_CANCEL_ALL;
import static android.service.notification.NotificationRankerService.REASON_CHANNEL_BANNED;
@@ -40,8 +42,6 @@
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_DEFAULT;
-import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_NONE;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -975,6 +975,7 @@
mUsageStats = new NotificationUsageStats(getContext());
mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
mRankingHelper = new RankingHelper(getContext(),
+ getContext().getPackageManager(),
mRankingHandler,
mUsageStats,
extractorNames);
@@ -1538,8 +1539,7 @@
@Override
public void setImportance(String pkg, int uid, int importance) {
enforceSystemOrSystemUI("Caller not system or systemui");
- setNotificationsEnabledForPackageImpl(pkg, uid,
- importance != NotificationListenerService.Ranking.IMPORTANCE_NONE);
+ setNotificationsEnabledForPackageImpl(pkg, uid, importance != IMPORTANCE_NONE);
mRankingHelper.setImportance(pkg, uid, importance);
savePolicyFile();
}
@@ -1567,21 +1567,6 @@
}
@Override
- public void updateNotificationChannel(String pkg, NotificationChannel channel) {
- Preconditions.checkNotNull(channel);
- Preconditions.checkNotNull(channel.getId());
- checkCallerIsSystemOrSameApp(pkg);
- if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
- // cancel
- cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
- UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
- }
- mRankingHelper.updateNotificationChannel(Binder.getCallingUid(), pkg,
- Binder.getCallingUid(), channel);
- savePolicyFile();
- }
-
- @Override
public NotificationChannel getNotificationChannel(String pkg, String channelId) {
Preconditions.checkNotNull(channelId);
checkCallerIsSystemOrSameApp(pkg);
@@ -1620,7 +1605,7 @@
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
}
- mRankingHelper.updateNotificationChannel(Binder.getCallingUid(), pkg, uid, channel);
+ mRankingHelper.updateNotificationChannel(pkg, uid, channel);
savePolicyFile();
}
@@ -2814,7 +2799,7 @@
// setup local book-keeping
final NotificationRecord r = new NotificationRecord(getContext(), n,
- mRankingHelper.getNotificationChannel(pkg, callingUid,
+ mRankingHelper.getNotificationChannelWithFallback(pkg, callingUid,
n.getNotification().getNotificationChannel()));
mHandler.post(new EnqueueNotificationRunnable(userId, r));
@@ -3022,7 +3007,8 @@
final String key = record.getKey();
// Should this notification make noise, vibe, or use the LED?
- final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_DEFAULT;
+ final boolean aboveThreshold =
+ record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
if (DBG || record.isIntercepted())
Slog.v(TAG,
@@ -3076,8 +3062,8 @@
} else if (notification.sound != null) {
soundUri = notification.sound;
hasValidSound = (soundUri != null);
- } else if (record.getChannel().getDefaultRingtone() != null) {
- soundUri = record.getChannel().getDefaultRingtone();
+ } else if (record.getChannel().getRingtone() != null) {
+ soundUri = record.getChannel().getRingtone();
hasValidSound = (soundUri != null);
}
@@ -3094,9 +3080,7 @@
// The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
final boolean useDefaultVibrate =
(notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
-
final boolean hasChannelVibration = record.getChannel().shouldVibrate();
-
hasValidVibrate = useDefaultVibrate || convertSoundToVibration ||
hasCustomVibrate || hasChannelVibration;
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index c5de93e..a1256db 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -15,12 +15,11 @@
*/
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;
-import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_LOW;
-import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_MAX;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -30,7 +29,6 @@
import android.graphics.Bitmap;
import android.graphics.drawable.Icon;
import android.media.AudioAttributes;
-import android.net.Uri;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
@@ -140,10 +138,8 @@
importance = IMPORTANCE_DEFAULT;
break;
case Notification.PRIORITY_HIGH:
- importance = IMPORTANCE_HIGH;
- break;
case Notification.PRIORITY_MAX:
- importance = IMPORTANCE_MAX;
+ importance = IMPORTANCE_HIGH;
break;
}
stats.requestedImportance = importance;
@@ -153,7 +149,7 @@
|| n.sound != null
|| n.vibrate != null
|| mNotificationChannel.shouldVibrate()
- || mNotificationChannel.getDefaultRingtone() != null;
+ || mNotificationChannel.getRingtone() != null;
stats.isNoisy = isNoisy;
if (!isNoisy && importance > IMPORTANCE_LOW) {
@@ -167,7 +163,7 @@
}
if (n.fullScreenIntent != null) {
- importance = IMPORTANCE_MAX;
+ importance = IMPORTANCE_HIGH;
}
stats.naturalImportance = importance;
@@ -319,10 +315,11 @@
@Override
public final String toString() {
return String.format(
- "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s importance=%d key=%s: %s)",
+ "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s importance=%d key=%s" +
+ " channel=%s: %s)",
System.identityHashCode(this),
this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(),
- this.sbn.getTag(), this.mImportance, this.sbn.getKey(),
+ this.sbn.getTag(), this.mImportance, this.sbn.getKey(), this.getChannel().getId(),
this.sbn.getNotification());
}
@@ -384,7 +381,7 @@
}
private void applyUserImportance() {
- if (mUserImportance != NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED) {
+ if (mUserImportance != IMPORTANCE_UNSPECIFIED) {
mImportance = mUserImportance;
mImportanceExplanation = getUserExplanation();
}
@@ -395,7 +392,7 @@
}
public void setImportance(int importance, CharSequence explanation) {
- if (importance != NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED) {
+ if (importance != IMPORTANCE_UNSPECIFIED) {
mImportance = importance;
mImportanceExplanation = explanation;
}
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index 34c5283..e8cf6a1 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -16,7 +16,7 @@
package com.android.server.notification;
-import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
import android.app.Notification;
import android.content.ContentValues;
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 2df4043..cb5fb0d 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -33,8 +33,10 @@
int getImportance(String packageName, int uid);
void createNotificationChannel(String pkg, int uid, NotificationChannel channel);
- void updateNotificationChannel(int callingUid, String pkg, int uid, NotificationChannel channel);
+ void updateNotificationChannel(String pkg, int uid, NotificationChannel channel);
+ void updateNotificationChannelFromRanker(String pkg, int uid, NotificationChannel channel);
NotificationChannel getNotificationChannel(String pkg, int uid, String channelId);
+ NotificationChannel getNotificationChannelWithFallback(String pkg, int uid, String channelId);
void deleteNotificationChannel(String pkg, int uid, String channelId);
ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid);
}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index d59115e..d11d4de 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -15,12 +15,17 @@
*/
package com.android.server.notification;
+import com.android.internal.R;
+
import android.app.Notification;
import android.app.NotificationChannel;
+import android.app.NotificationManager;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ParceledListSlice;
+import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService.Ranking;
@@ -28,8 +33,6 @@
import android.util.ArrayMap;
import android.util.Slog;
-import com.android.internal.R;
-
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -63,8 +66,8 @@
private static final String ATT_IMPORTANCE = "importance";
private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
- private static final int DEFAULT_VISIBILITY = Ranking.VISIBILITY_NO_OVERRIDE;
- private static final int DEFAULT_IMPORTANCE = Ranking.IMPORTANCE_UNSPECIFIED;
+ private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
+ private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED;
private final NotificationSignalExtractor[] mSignalExtractors;
private final NotificationComparator mPreliminaryComparator = new NotificationComparator();
@@ -76,11 +79,13 @@
private final Context mContext;
private final RankingHandler mRankingHandler;
+ private final PackageManager mPm;
- public RankingHelper(Context context, RankingHandler rankingHandler,
+ public RankingHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
NotificationUsageStats usageStats, String[] extractorNames) {
mContext = context;
mRankingHandler = rankingHandler;
+ mPm = pm;
final int N = extractorNames.length;
mSignalExtractors = new NotificationSignalExtractor[N];
@@ -131,7 +136,6 @@
public void readXml(XmlPullParser parser, boolean forRestore)
throws XmlPullParserException, IOException {
- final PackageManager pm = mContext.getPackageManager();
int type = parser.getEventType();
if (type != XmlPullParser.START_TAG) return;
String tag = parser.getName();
@@ -147,31 +151,24 @@
if (TAG_PACKAGE.equals(tag)) {
int uid = safeInt(parser, ATT_UID, Record.UNKNOWN_UID);
String name = parser.getAttributeValue(null, ATT_NAME);
+ int importance = safeInt(parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
if (!TextUtils.isEmpty(name)) {
if (forRestore) {
try {
//TODO: http://b/22388012
- uid = pm.getPackageUidAsUser(name, UserHandle.USER_SYSTEM);
+ uid = mPm.getPackageUidAsUser(name, UserHandle.USER_SYSTEM);
} catch (NameNotFoundException e) {
// noop
}
}
- Record r = null;
- if (uid == Record.UNKNOWN_UID) {
- r = mRestoredWithoutUids.get(name);
- if (r == null) {
- r = new Record();
- r.pkg = name;
- mRestoredWithoutUids.put(name, r);
- }
- } else {
- r = getOrCreateRecord(name, uid);
- }
- r.importance = safeInt(parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
- r.priority = safeInt(parser, ATT_PRIORITY, DEFAULT_PRIORITY);
- r.visibility = safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
+ Record r = getOrCreateRecord(name, uid,
+ safeInt(parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE),
+ safeInt(parser, ATT_PRIORITY, DEFAULT_PRIORITY),
+ safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY));
+
+ // Channels
final int innerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
@@ -184,15 +181,19 @@
if (TAG_CHANNEL.equals(tagName)) {
String id = parser.getAttributeValue(null, ATT_ID);
CharSequence channelName = parser.getAttributeValue(null, ATT_NAME);
+ int channelImportance =
+ safeInt(parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
if (!TextUtils.isEmpty(id)) {
- final NotificationChannel channel =
- new NotificationChannel(id, channelName);
+ final NotificationChannel channel = new NotificationChannel(id,
+ channelName, channelImportance);
channel.populateFromXml(parser);
r.channels.put(id, channel);
}
}
}
+
+ clampDefaultChannel(r);
}
}
}
@@ -204,23 +205,77 @@
return pkg + "|" + uid;
}
- private Record getOrCreateRecord(String pkg, int uid) {
+ private Record getRecord(String pkg, int uid) {
final String key = recordKey(pkg, uid);
- Record r = mRecords.get(key);
+ return mRecords.get(key);
+ }
+
+ private Record getOrCreateRecord(String pkg, int uid) {
+ return getOrCreateRecord(pkg, uid,
+ DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY);
+ }
+
+ private Record getOrCreateRecord(String pkg, int uid, int importance, int priority,
+ int visibility) {
+ final String key = recordKey(pkg, uid);
+ Record r = (uid == Record.UNKNOWN_UID) ? mRestoredWithoutUids.get(pkg) : mRecords.get(key);
if (r == null) {
r = new Record();
r.pkg = pkg;
r.uid = uid;
- NotificationChannel defaultChannel = createDefaultChannel();
- r.channels.put(defaultChannel.getId(), defaultChannel);
- mRecords.put(key, r);
+ r.importance = importance;
+ r.priority = priority;
+ r.visibility = visibility;
+ createDefaultChannelIfMissing(r);
+ if (r.uid == Record.UNKNOWN_UID) {
+ mRestoredWithoutUids.put(pkg, r);
+ } else {
+ mRecords.put(key, r);
+ }
}
+ clampDefaultChannel(r);
return r;
}
- private NotificationChannel createDefaultChannel() {
- return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID,
- mContext.getString(R.string.default_notification_channel_label));
+ // Clamp the importance level of the default channel for apps targeting the new SDK version,
+ // unless the user has already changed the importance.
+ private void clampDefaultChannel(Record r) {
+ try {
+ final ApplicationInfo applicationInfo = mPm.getApplicationInfo(r.pkg, 0);
+ if (applicationInfo.targetSdkVersion > Build.VERSION_CODES.N_MR1) {
+ final NotificationChannel defaultChannel =
+ r.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID);
+ if ((defaultChannel.getUserLockedFields()
+ & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0) {
+ defaultChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
+ updateConfig();
+ }
+ }
+ } catch (NameNotFoundException e) {
+ // oh well.
+ }
+ }
+
+ private void createDefaultChannelIfMissing(Record r) {
+ if (!r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
+ NotificationChannel channel;
+ channel = new NotificationChannel(
+ NotificationChannel.DEFAULT_CHANNEL_ID,
+ mContext.getString(R.string.default_notification_channel_label),
+ r.importance);
+ channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
+ channel.setLockscreenVisibility(r.visibility);
+ if (r.importance != NotificationManager.IMPORTANCE_UNSPECIFIED) {
+ channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
+ }
+ if (r.priority != DEFAULT_PRIORITY) {
+ channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
+ }
+ if (r.visibility != DEFAULT_VISIBILITY) {
+ channel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
+ }
+ r.channels.put(channel.getId(), channel);
+ }
}
public void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
@@ -397,6 +452,10 @@
mContext.getString(R.string.default_notification_channel_label))) {
throw new IllegalArgumentException("Channel already exists");
}
+ if (channel.getImportance() < NotificationManager.IMPORTANCE_NONE
+ || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) {
+ throw new IllegalArgumentException("Invalid importance level");
+ }
if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
channel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE);
}
@@ -405,17 +464,12 @@
}
@Override
- public void updateNotificationChannel(int callingUid, String pkg, int uid,
- NotificationChannel updatedChannel) {
+ public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel) {
Record r = getOrCreateRecord(pkg, uid);
NotificationChannel channel = r.channels.get(updatedChannel.getId());
if (channel == null) {
throw new IllegalArgumentException("Channel does not exist");
}
- if (!isUidSystem(callingUid)) {
- updatedChannel.setImportance(channel.getImportance());
- updatedChannel.setName(channel.getName());
- }
if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
updatedChannel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE);
}
@@ -424,6 +478,57 @@
}
@Override
+ public void updateNotificationChannelFromRanker(String pkg, int uid,
+ NotificationChannel updatedChannel) {
+ Record r = getOrCreateRecord(pkg, uid);
+ NotificationChannel channel = r.channels.get(updatedChannel.getId());
+ if (channel == null) {
+ throw new IllegalArgumentException("Channel does not exist");
+ }
+
+ if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0) {
+ channel.setImportance(updatedChannel.getImportance());
+ }
+ if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_LIGHTS) == 0) {
+ channel.setLights(updatedChannel.shouldShowLights());
+ }
+ if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_PRIORITY) == 0) {
+ channel.setBypassDnd(updatedChannel.canBypassDnd());
+ }
+ if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_RINGTONE) == 0) {
+ channel.setRingtone(updatedChannel.getRingtone());
+ }
+ if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_VIBRATION) == 0) {
+ channel.setVibration(updatedChannel.shouldVibrate());
+ }
+ if ((channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_VISIBILITY) == 0) {
+ if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
+ channel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE);
+ } else {
+ channel.setLockscreenVisibility(updatedChannel.getLockscreenVisibility());
+ }
+ }
+
+ r.channels.put(channel.getId(), channel);
+ updateConfig();
+ }
+
+ @Override
+ public NotificationChannel getNotificationChannelWithFallback(String pkg, int uid,
+ String channelId) {
+ Record r = getOrCreateRecord(pkg, uid);
+ if (channelId == null) {
+ channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
+ }
+ NotificationChannel channel = r.channels.get(channelId);
+ if (channel != null) {
+ return channel;
+ } else {
+ return r.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID);
+ }
+ }
+
+ @Override
public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId) {
Record r = getOrCreateRecord(pkg, uid);
if (channelId == null) {
@@ -434,8 +539,10 @@
@Override
public void deleteNotificationChannel(String pkg, int uid, String channelId) {
- Record r = getOrCreateRecord(pkg, uid);
- r.channels.remove(channelId);
+ Record r = getRecord(pkg, uid);
+ if (r != null) {
+ r.channels.remove(channelId);
+ }
}
@Override
@@ -446,7 +553,7 @@
for (int i = 0; i < N; i++) {
channels.add(r.channels.valueAt(i));
}
- return new ParceledListSlice<NotificationChannel>(channels);
+ return new ParceledListSlice<>(channels);
}
/**
@@ -459,11 +566,12 @@
}
public void setEnabled(String packageName, int uid, boolean enabled) {
- boolean wasEnabled = getImportance(packageName, uid) != Ranking.IMPORTANCE_NONE;
+ boolean wasEnabled = getImportance(packageName, uid) != NotificationManager.IMPORTANCE_NONE;
if (wasEnabled == enabled) {
return;
}
- setImportance(packageName, uid, enabled ? DEFAULT_IMPORTANCE : Ranking.IMPORTANCE_NONE);
+ setImportance(packageName, uid,
+ enabled ? DEFAULT_IMPORTANCE : NotificationManager.IMPORTANCE_NONE);
}
public void dump(PrintWriter pw, String prefix, NotificationManagerService.DumpFilter filter) {
@@ -599,7 +707,7 @@
ArrayMap<Integer, String> packageBans = new ArrayMap<>(N);
for (int i = 0; i < N; i++) {
final Record r = mRecords.valueAt(i);
- if (r.importance == Ranking.IMPORTANCE_NONE) {
+ if (r.importance == NotificationManager.IMPORTANCE_NONE) {
packageBans.put(r.uid, r.pkg);
}
}
@@ -611,14 +719,13 @@
|| mRestoredWithoutUids.isEmpty()) {
return; // nothing to do
}
- final PackageManager pm = mContext.getPackageManager();
boolean updated = false;
for (String pkg : pkgList) {
final Record r = mRestoredWithoutUids.get(pkg);
if (r != null) {
try {
//TODO: http://b/22388012
- r.uid = pm.getPackageUidAsUser(r.pkg, UserHandle.USER_SYSTEM);
+ r.uid = mPm.getPackageUidAsUser(r.pkg, UserHandle.USER_SYSTEM);
mRestoredWithoutUids.remove(pkg);
mRecords.put(recordKey(r.pkg, r.uid), r);
updated = true;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 06401e0..2fef928 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1035,7 +1035,10 @@
mWindowAnimationScaleSetting = Settings.Global.getFloat(context.getContentResolver(),
Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScaleSetting);
mTransitionAnimationScaleSetting = Settings.Global.getFloat(context.getContentResolver(),
- Settings.Global.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScaleSetting);
+ Settings.Global.TRANSITION_ANIMATION_SCALE,
+ context.getResources().getFloat(
+ R.dimen.config_appTransitionAnimationDurationScaleDefault));
+
setAnimatorDurationScale(Settings.Global.getFloat(context.getContentResolver(),
Settings.Global.ANIMATOR_DURATION_SCALE, mAnimatorDurationScaleSetting));
diff --git a/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 5fe000a..4570a4b 100644
--- a/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -229,7 +229,8 @@
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid, mPid,
mScore, n, mUser, System.currentTimeMillis());
NotificationRecord r = new NotificationRecord(getContext(), sbn,
- new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "misc"));
+ new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "misc",
+ NotificationManager.IMPORTANCE_DEFAULT));
mService.addNotification(r);
return r;
}
@@ -334,7 +335,7 @@
@Test
public void testBeepFromChannel() throws Exception {
NotificationRecord r = getQuietNotification();
- r.getChannel().setDefaultRingtone(Settings.System.DEFAULT_NOTIFICATION_URI);
+ r.getChannel().setRingtone(Settings.System.DEFAULT_NOTIFICATION_URI);
r.setImportance(NotificationManager.IMPORTANCE_DEFAULT, "for testing");
mService.buzzBeepBlinkLocked(r);
@@ -388,7 +389,7 @@
@Test
public void testChannelNoOverwriteCustomBeep() throws Exception {
NotificationRecord r = getCustomBeepyNotification();
- r.getChannel().setDefaultRingtone(Settings.System.DEFAULT_RINGTONE_URI);
+ r.getChannel().setRingtone(Settings.System.DEFAULT_RINGTONE_URI);
mService.buzzBeepBlinkLocked(r);
@@ -408,7 +409,7 @@
@Test
public void testNoInterruptionForMin() throws Exception {
NotificationRecord r = getBeepyNotification();
- r.setImportance(Ranking.IMPORTANCE_MIN, "foo");
+ r.setImportance(NotificationManager.IMPORTANCE_MIN, "foo");
mService.buzzBeepBlinkLocked(r);
diff --git a/services/tests/servicestests/src/com/android/server/notification/ImportanceExtractorTest.java b/services/tests/servicestests/src/com/android/server/notification/ImportanceExtractorTest.java
index 3cbde1d..305b5e0 100644
--- a/services/tests/servicestests/src/com/android/server/notification/ImportanceExtractorTest.java
+++ b/services/tests/servicestests/src/com/android/server/notification/ImportanceExtractorTest.java
@@ -90,8 +90,8 @@
when(mConfig.getImportance(anyString(), anyInt())).thenReturn(
NotificationManager.IMPORTANCE_MIN);
- NotificationChannel channel = new NotificationChannel("a", "a");
- channel.setImportance(NotificationManager.IMPORTANCE_UNSPECIFIED);
+ NotificationChannel channel =
+ new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED);
NotificationRecord r = getNotificationRecord(channel);
@@ -107,8 +107,8 @@
when(mConfig.getImportance(anyString(), anyInt())).thenReturn(
NotificationManager.IMPORTANCE_MIN);
- NotificationChannel channel = new NotificationChannel("a", "a");
- channel.setImportance(NotificationManager.IMPORTANCE_HIGH);
+ NotificationChannel channel =
+ new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_HIGH);
NotificationRecord r = getNotificationRecord(channel);
@@ -124,8 +124,8 @@
when(mConfig.getImportance(anyString(), anyInt())).thenReturn(
NotificationManager.IMPORTANCE_HIGH);
- NotificationChannel channel = new NotificationChannel("a", "a");
- channel.setImportance(NotificationManager.IMPORTANCE_MIN);
+ NotificationChannel channel =
+ new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_MIN);
NotificationRecord r = getNotificationRecord(channel);
@@ -141,8 +141,8 @@
when(mConfig.getImportance(anyString(), anyInt())).thenReturn(
NotificationManager.IMPORTANCE_UNSPECIFIED);
- NotificationChannel channel = new NotificationChannel("a", "a");
- channel.setImportance(NotificationManager.IMPORTANCE_MIN);
+ NotificationChannel channel =
+ new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_MIN);
NotificationRecord r = getNotificationRecord(channel);
@@ -158,8 +158,8 @@
when(mConfig.getImportance(anyString(), anyInt())).thenReturn(
NotificationManager.IMPORTANCE_UNSPECIFIED);
- NotificationChannel channel = new NotificationChannel("a", "a");
- channel.setImportance(NotificationManager.IMPORTANCE_UNSPECIFIED);
+ NotificationChannel channel =
+ new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED);
NotificationRecord r = getNotificationRecord(channel);
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 ee33dcc..ad432d1 100644
--- a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -29,7 +29,10 @@
import android.content.Context;
import android.app.NotificationChannel;
import android.app.NotificationManager;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.net.Uri;
+import android.os.Build;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.support.test.InstrumentationRegistry;
@@ -44,14 +47,19 @@
import java.util.ArrayList;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.when;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class RankingHelperTest {
@Mock NotificationUsageStats mUsageStats;
@Mock RankingHandler handler;
+ @Mock PackageManager mPm;
private Notification mNotiGroupGSortA;
private Notification mNotiGroupGSortB;
@@ -64,6 +72,8 @@
private NotificationRecord mRecordNoGroup2;
private NotificationRecord mRecordNoGroupSortA;
private RankingHelper mHelper;
+ private final String pkg = "com.android.server.notification";
+ private final int uid = 0;
private Context getContext() {
return InstrumentationRegistry.getTargetContext();
@@ -74,7 +84,7 @@
MockitoAnnotations.initMocks(this);
UserHandle user = UserHandle.ALL;
- mHelper = new RankingHelper(getContext(), handler, mUsageStats,
+ mHelper = new RankingHelper(getContext(), mPm, handler, mUsageStats,
new String[] {ImportanceExtractor.class.getName()});
mNotiGroupGSortA = new Notification.Builder(getContext())
@@ -121,11 +131,36 @@
mRecordNoGroupSortA = new NotificationRecord(getContext(), new StatusBarNotification(
"package", "package", 1, null, 0, 0, 0, mNotiNoGroupSortA, user),
getDefaultChannel());
+
+ final ApplicationInfo legacy = new ApplicationInfo();
+ legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
+ try {
+ when(mPm.getApplicationInfo(anyString(), anyInt())).thenReturn(legacy);
+ } catch (PackageManager.NameNotFoundException e) {}
}
private NotificationChannel getDefaultChannel() {
- return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name");
- }
+ return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name",
+ NotificationManager.IMPORTANCE_LOW);
+ }
+
+ private ByteArrayOutputStream writeXmlAndPurge(String pkg, int uid, String... channelIds)
+ throws Exception {
+ XmlSerializer serializer = new FastXmlSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+ serializer.startDocument(null, true);
+ serializer.startTag(null, "ranking");
+ mHelper.writeXml(serializer, false);
+ serializer.endTag(null, "ranking");
+ serializer.endDocument();
+ serializer.flush();
+
+ for (String channelId : channelIds) {
+ mHelper.deleteNotificationChannel(pkg, uid, channelId);
+ }
+ return baos;
+ }
@Test
public void testFindAfterRankingWithASplitGroup() throws Exception {
@@ -179,12 +214,11 @@
@Test
public void testChannelXml() throws Exception {
- String pkg = "com.android.server.notification";
- int uid = 0;
- NotificationChannel channel1 = new NotificationChannel("id1", "name1");
- NotificationChannel channel2 = new NotificationChannel("id2", "name2");
- channel2.setImportance(NotificationManager.IMPORTANCE_LOW);
- channel2.setDefaultRingtone(new Uri.Builder().scheme("test").build());
+ NotificationChannel channel1 =
+ new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+ NotificationChannel channel2 =
+ new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW);
+ channel2.setRingtone(new Uri.Builder().scheme("test").build());
channel2.setLights(true);
channel2.setBypassDnd(true);
channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
@@ -192,23 +226,16 @@
mHelper.createNotificationChannel(pkg, uid, channel1);
mHelper.createNotificationChannel(pkg, uid, channel2);
- byte[] data;
- XmlSerializer serializer = new FastXmlSerializer();
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
- serializer.startDocument(null, true);
- serializer.startTag(null, "ranking");
- mHelper.writeXml(serializer, false);
- serializer.endTag(null, "ranking");
- serializer.endDocument();
- serializer.flush();
+ ByteArrayOutputStream baos = writeXmlAndPurge(pkg, uid, channel1.getId(), channel2.getId(),
+ NotificationChannel.DEFAULT_CHANNEL_ID);
mHelper.deleteNotificationChannel(pkg, uid, channel1.getId());
mHelper.deleteNotificationChannel(pkg, uid, channel2.getId());
mHelper.deleteNotificationChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID);
XmlPullParser parser = Xml.newPullParser();
- parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), null);
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
+ null);
parser.nextTag();
mHelper.readXml(parser, false);
@@ -217,4 +244,146 @@
assertNotNull(
mHelper.getNotificationChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID));
}
+
+ @Test
+ public void testChannelXml_defaultChannelLegacyApp_noUserSettings() throws Exception {
+ NotificationChannel channel1 =
+ new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_DEFAULT);
+
+ mHelper.createNotificationChannel(pkg, uid, channel1);
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(pkg, uid, channel1.getId(),
+ NotificationChannel.DEFAULT_CHANNEL_ID);
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
+ null);
+ parser.nextTag();
+ mHelper.readXml(parser, false);
+
+ final NotificationChannel updated =
+ mHelper.getNotificationChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID);
+ assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, updated.getImportance());
+ assertFalse(updated.canBypassDnd());
+ assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,updated.getLockscreenVisibility());
+ assertEquals(0, updated.getUserLockedFields());
+ }
+
+ @Test
+ public void testChannelXml_defaultChannelUpdatedApp() throws Exception {
+ final ApplicationInfo updated = new ApplicationInfo();
+ updated.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1;
+ when(mPm.getApplicationInfo(anyString(), anyInt())).thenReturn(updated);
+
+ NotificationChannel channel1 =
+ new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_MIN);
+
+ mHelper.createNotificationChannel(pkg, uid, channel1);
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(pkg, uid, channel1.getId(),
+ NotificationChannel.DEFAULT_CHANNEL_ID);
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
+ null);
+ parser.nextTag();
+ mHelper.readXml(parser, false);
+
+ assertEquals(NotificationManager.IMPORTANCE_LOW, mHelper.getNotificationChannel(
+ pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID).getImportance());
+ }
+
+ // TODO: test with hardcoded N xml.
+ @Test
+ public void testChannelXml_upgradeCreateDefaultChannel() throws Exception {
+ mHelper.setImportance(pkg, uid, NotificationManager.IMPORTANCE_HIGH);
+ mHelper.setPriority(pkg, uid, Notification.PRIORITY_MAX);
+ mHelper.setVisibilityOverride(pkg, uid, Notification.VISIBILITY_SECRET);
+ // pre-O xml won't have channels.
+ mHelper.deleteNotificationChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID);
+
+ ByteArrayOutputStream baos =
+ writeXmlAndPurge(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID);
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
+ null);
+ parser.nextTag();
+ mHelper.readXml(parser, false);
+
+ final NotificationChannel updated =
+ mHelper.getNotificationChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID);
+ assertEquals(NotificationManager.IMPORTANCE_HIGH, updated.getImportance());
+ assertTrue(updated.canBypassDnd());
+ assertEquals(Notification.VISIBILITY_SECRET, updated.getLockscreenVisibility());
+ assertEquals(NotificationChannel.USER_LOCKED_IMPORTANCE
+ | NotificationChannel.USER_LOCKED_PRIORITY
+ | NotificationChannel.USER_LOCKED_VISIBILITY, updated.getUserLockedFields());
+ }
+
+ // TODO: test lock individually.
+ @Test
+ public void testUpdate_userLockedChannelFields() throws Exception {
+ // all fields locked by user
+ final NotificationChannel channel =
+ new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW);
+ channel.setRingtone(new Uri.Builder().scheme("test").build());
+ channel.setLights(true);
+ channel.setBypassDnd(true);
+ channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+ channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE
+ | NotificationChannel.USER_LOCKED_VISIBILITY
+ | NotificationChannel.USER_LOCKED_VIBRATION
+ | NotificationChannel.USER_LOCKED_LIGHTS
+ | NotificationChannel.USER_LOCKED_PRIORITY
+ | NotificationChannel.USER_LOCKED_RINGTONE);
+
+ mHelper.createNotificationChannel(pkg, uid, channel);
+
+ // same id, try to update all fields
+ final NotificationChannel channel2 =
+ new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
+ channel2.setRingtone(new Uri.Builder().scheme("test2").build());
+ channel2.setLights(false);
+ channel2.setBypassDnd(false);
+ channel2.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
+
+ mHelper.updateNotificationChannelFromRanker(pkg, uid, channel2);
+
+ // no fields should be changed
+ assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId()));
+ }
+
+ @Test
+ public void testUpdate() throws Exception {
+ // no fields locked by user
+ final NotificationChannel channel =
+ new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW);
+ channel.setRingtone(new Uri.Builder().scheme("test").build());
+ channel.setLights(true);
+ channel.setBypassDnd(true);
+ channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+
+ mHelper.createNotificationChannel(pkg, uid, channel);
+
+ // same id, try to update all fields
+ final NotificationChannel channel2 =
+ new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
+ channel2.setRingtone(new Uri.Builder().scheme("test2").build());
+ channel2.setLights(false);
+ channel2.setBypassDnd(false);
+ channel2.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
+
+ mHelper.updateNotificationChannel(pkg, uid, channel2);
+
+ // all fields should be changed
+ assertEquals(channel2, mHelper.getNotificationChannel(pkg, uid, channel.getId()));
+ }
+
+ @Test
+ public void testGetChannelWithFallback() throws Exception {
+ NotificationChannel channel =
+ mHelper.getNotificationChannelWithFallback(pkg, uid, "garbage");
+ assertEquals(NotificationChannel.DEFAULT_CHANNEL_ID, channel.getId());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/servicestests/src/com/android/server/notification/SnoozeHelperTest.java
index ec1fdad..ffc45ea 100644
--- a/services/tests/servicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -24,6 +24,7 @@
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationChannel;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.os.UserHandle;
@@ -178,7 +179,8 @@
.build();
return new NotificationRecord(getContext(), new StatusBarNotification(
pkg, pkg, id, tag, 0, 0, 0, n, user),
- new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name"));
+ new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name",
+ NotificationManager.IMPORTANCE_HIGH));
}
}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index af8c314..3daa87c 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -597,7 +597,6 @@
mAccessoryModeRequestTime + ACCESSORY_REQUEST_TIMEOUT;
if (mConfigured && enteringAccessoryMode) {
- mAccessoryModeRequestTime = 0;
// successfully entered accessory mode
if (mAccessoryStrings != null) {