diff options
268 files changed, 7598 insertions, 4239 deletions
diff --git a/Android.bp b/Android.bp index eb9cbbb136f9..93c94e36ed36 100644 --- a/Android.bp +++ b/Android.bp @@ -872,6 +872,7 @@ aidl_interface { local_include_dir: "core/java", srcs: [ "core/java/android/net/INetworkStackConnector.aidl", + "core/java/android/net/dhcp/DhcpServingParamsParcel.aidl", ], api_dir: "aidl/networkstack", } diff --git a/api/current.txt b/api/current.txt index b11e6cdaa635..16652bfe97f5 100644 --- a/api/current.txt +++ b/api/current.txt @@ -10200,6 +10200,7 @@ package android.content { field public static final java.lang.String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED"; field public static final java.lang.String ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED"; field public static final java.lang.String ACTION_PASTE = "android.intent.action.PASTE"; + field public static final java.lang.String ACTION_PERMISSION_USAGE_DETAILS = "android.intent.action.PERMISSION_USAGE_DETAILS"; field public static final java.lang.String ACTION_PICK = "android.intent.action.PICK"; field public static final java.lang.String ACTION_PICK_ACTIVITY = "android.intent.action.PICK_ACTIVITY"; field public static final java.lang.String ACTION_POWER_CONNECTED = "android.intent.action.ACTION_POWER_CONNECTED"; @@ -10324,6 +10325,7 @@ package android.content { field public static final java.lang.String EXTRA_NOT_UNKNOWN_SOURCE = "android.intent.extra.NOT_UNKNOWN_SOURCE"; field public static final java.lang.String EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI"; field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME"; + field public static final java.lang.String EXTRA_PERMISSION_USAGE_PERMISSIONS = "android.intent.extra.PERMISSION_USAGE_PERMISSIONS"; field public static final java.lang.String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER"; field public static final java.lang.String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT"; field public static final java.lang.String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY"; @@ -27146,12 +27148,15 @@ package android.media.session { method public void onSessionEvent(java.lang.String, android.os.Bundle); } - public static final class MediaController.PlaybackInfo { + public static final class MediaController.PlaybackInfo implements android.os.Parcelable { + method public int describeContents(); method public android.media.AudioAttributes getAudioAttributes(); method public int getCurrentVolume(); method public int getMaxVolume(); method public int getPlaybackType(); method public int getVolumeControl(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.media.session.MediaController.PlaybackInfo> CREATOR; field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1 field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2 } @@ -42759,10 +42764,10 @@ package android.telecom { ctor public CallRedirectionService(); method public final void cancelCall(); method public final android.os.IBinder onBind(android.content.Intent); - method public abstract void onPlaceCall(android.net.Uri, android.telecom.PhoneAccountHandle); + method public abstract void onPlaceCall(android.net.Uri, android.telecom.PhoneAccountHandle, boolean); method public final boolean onUnbind(android.content.Intent); method public final void placeCallUnmodified(); - method public final void redirectCall(android.net.Uri, android.telecom.PhoneAccountHandle); + method public final void redirectCall(android.net.Uri, android.telecom.PhoneAccountHandle, boolean); field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.CallRedirectionService"; } diff --git a/api/system-current.txt b/api/system-current.txt index e99ac06c258e..9ce25f52957d 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -927,6 +927,9 @@ package android.app.usage { method public java.util.Map<java.lang.String, java.lang.Integer> getAppStandbyBuckets(); method public void registerAppUsageObserver(int, java.lang.String[], long, java.util.concurrent.TimeUnit, android.app.PendingIntent); method public void registerUsageSessionObserver(int, java.lang.String[], long, java.util.concurrent.TimeUnit, long, java.util.concurrent.TimeUnit, android.app.PendingIntent, android.app.PendingIntent); + method public void reportUsageStart(android.app.Activity, java.lang.String); + method public void reportUsageStart(android.app.Activity, java.lang.String, long); + method public void reportUsageStop(android.app.Activity, java.lang.String); method public void setAppStandbyBucket(java.lang.String, int); method public void setAppStandbyBuckets(java.util.Map<java.lang.String, java.lang.Integer>); method public void unregisterAppUsageObserver(int); @@ -4491,6 +4494,7 @@ package android.os { field public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10; // 0xa field public static final int PAYLOAD_MISMATCHED_TYPE_ERROR = 6; // 0x6 field public static final int PAYLOAD_SIZE_MISMATCH_ERROR = 11; // 0xb + field public static final int PAYLOAD_TIMESTAMP_ERROR = 51; // 0x33 field public static final int POST_INSTALL_RUNNER_ERROR = 5; // 0x5 field public static final int SUCCESS = 0; // 0x0 field public static final int UPDATED_BUT_NOT_ACTIVE = 52; // 0x34 @@ -4595,6 +4599,17 @@ package android.os.storage { package android.permission { + public final class PermissionControllerManager { + method public void revokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>, boolean, int, java.util.concurrent.Executor, android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback); + field public static final int REASON_INSTALLER_POLICY_VIOLATION = 2; // 0x2 + field public static final int REASON_MALWARE = 1; // 0x1 + } + + public static abstract class PermissionControllerManager.OnRevokeRuntimePermissionsCallback { + ctor public PermissionControllerManager.OnRevokeRuntimePermissionsCallback(); + method public abstract void onRevokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>); + } + public abstract class PermissionControllerService extends android.app.Service { ctor public PermissionControllerService(); method public final void attachBaseContext(android.content.Context); @@ -4602,6 +4617,7 @@ package android.permission { method public abstract int onCountPermissionApps(java.util.List<java.lang.String>, boolean, boolean); method public abstract java.util.List<android.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(java.lang.String); method public abstract void onRevokeRuntimePermission(java.lang.String, java.lang.String); + method public abstract java.util.Map<java.lang.String, java.util.List<java.lang.String>> onRevokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>, boolean, int, java.lang.String); field public static final java.lang.String SERVICE_INTERFACE = "android.permission.PermissionControllerService"; } @@ -5127,6 +5143,8 @@ package android.service.autofill.augmented { public abstract class AugmentedAutofillService extends android.app.Service { ctor public AugmentedAutofillService(); + method protected final void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]); + method protected void dump(java.io.PrintWriter, java.lang.String[]); method public void onFillRequest(android.service.autofill.augmented.FillRequest, android.os.CancellationSignal, android.service.autofill.augmented.FillController, android.service.autofill.augmented.FillCallback); field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.augmented.AugmentedAutofillService"; } diff --git a/api/test-current.txt b/api/test-current.txt index 1401cbb4211e..de643501d16c 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -985,6 +985,21 @@ package android.os.strictmode { } +package android.permission { + + public final class PermissionControllerManager { + method public void revokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>, boolean, int, java.util.concurrent.Executor, android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback); + field public static final int REASON_INSTALLER_POLICY_VIOLATION = 2; // 0x2 + field public static final int REASON_MALWARE = 1; // 0x1 + } + + public static abstract class PermissionControllerManager.OnRevokeRuntimePermissionsCallback { + ctor public PermissionControllerManager.OnRevokeRuntimePermissionsCallback(); + method public abstract void onRevokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>); + } + +} + package android.print { public final class PrintJobInfo implements android.os.Parcelable { diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java index 74edffb4738d..b905e94cd96f 100644 --- a/cmds/input/src/com/android/commands/input/Input.java +++ b/cmds/input/src/com/android/commands/input/Input.java @@ -16,15 +16,20 @@ package com.android.commands.input; +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.INVALID_DISPLAY; + import android.hardware.input.InputManager; import android.os.SystemClock; -import android.util.Log; import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.ViewConfiguration; +import com.android.internal.os.BaseCommand; + +import java.io.PrintStream; import java.util.HashMap; import java.util.Map; @@ -33,9 +38,11 @@ import java.util.Map; * desired character output. */ -public class Input { +public class Input extends BaseCommand { private static final String TAG = "Input"; private static final String INVALID_ARGUMENTS = "Error: Invalid arguments for command: "; + private static final String INVALID_DISPLAY_ARGUMENTS = + "Error: Invalid arguments for display ID."; private static final Map<String, Integer> SOURCES = new HashMap<String, Integer>() {{ put("keyboard", InputDevice.SOURCE_KEYBOARD); @@ -50,6 +57,7 @@ public class Input { put("joystick", InputDevice.SOURCE_JOYSTICK); }}; + private static final Map<String, InputCmd> COMMANDS = new HashMap<String, InputCmd>(); /** * Command-line entry point. @@ -60,217 +68,256 @@ public class Input { (new Input()).run(args); } - private void run(String[] args) { - if (args.length < 1) { - showUsage(); - return; - } + Input() { + COMMANDS.put("text", new InputText()); + COMMANDS.put("keyevent", new InputKeyEvent()); + COMMANDS.put("tap", new InputTap()); + COMMANDS.put("swipe", new InputSwipe()); + COMMANDS.put("draganddrop", new InputDragAndDrop()); + COMMANDS.put("press", new InputPress()); + COMMANDS.put("roll", new InputRoll()); + } - int index = 0; - String command = args[index]; + @Override + public void onRun() throws Exception { + String arg = nextArgRequired(); int inputSource = InputDevice.SOURCE_UNKNOWN; - if (SOURCES.containsKey(command)) { - inputSource = SOURCES.get(command); - index++; - command = args[index]; + + // Get source (optional). + if (SOURCES.containsKey(arg)) { + inputSource = SOURCES.get(arg); + arg = nextArgRequired(); } - final int length = args.length - index; - - try { - if (command.equals("text")) { - if (length == 2) { - inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD); - sendText(inputSource, args[index+1]); - return; - } - } else if (command.equals("keyevent")) { - if (length >= 2) { - final boolean longpress = "--longpress".equals(args[index + 1]); - final int start = longpress ? index + 2 : index + 1; - inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD); - if (args.length > start) { - for (int i = start; i < args.length; i++) { - int keyCode = KeyEvent.keyCodeFromString(args[i]); - sendKeyEvent(inputSource, keyCode, longpress); - } - return; - } - } - } else if (command.equals("tap")) { - if (length == 3) { - inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN); - sendTap(inputSource, Float.parseFloat(args[index+1]), - Float.parseFloat(args[index+2])); - return; - } - } else if (command.equals("swipe")) { - int duration = -1; - inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN); - switch (length) { - case 6: - duration = Integer.parseInt(args[index+5]); - case 5: - sendSwipe(inputSource, - Float.parseFloat(args[index+1]), Float.parseFloat(args[index+2]), - Float.parseFloat(args[index+3]), Float.parseFloat(args[index+4]), - duration); - return; - } - } else if (command.equals("draganddrop")) { - int duration = -1; - inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN); - switch (length) { - case 6: - duration = Integer.parseInt(args[index+5]); - case 5: - sendDragAndDrop(inputSource, - Float.parseFloat(args[index+1]), Float.parseFloat(args[index+2]), - Float.parseFloat(args[index+3]), Float.parseFloat(args[index+4]), - duration); - return; - } - } else if (command.equals("press")) { - inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL); - if (length == 1) { - sendTap(inputSource, 0.0f, 0.0f); - return; - } - } else if (command.equals("roll")) { - inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL); - if (length == 3) { - sendMove(inputSource, Float.parseFloat(args[index+1]), - Float.parseFloat(args[index+2])); - return; - } - } else { - System.err.println("Error: Unknown command: " + command); - showUsage(); + + // Get displayId (optional). + int displayId = INVALID_DISPLAY; + if ("-d".equals(arg)) { + displayId = getDisplayId(); + arg = nextArgRequired(); + } + + // Get command and run. + InputCmd cmd = COMMANDS.get(arg); + if (cmd != null) { + try { + cmd.run(inputSource, displayId); return; + } catch (NumberFormatException ex) { + throw new IllegalArgumentException(INVALID_ARGUMENTS + arg); } - } catch (NumberFormatException ex) { } - System.err.println(INVALID_ARGUMENTS + command); - showUsage(); + + throw new IllegalArgumentException("Error: Unknown command: " + arg); } - /** - * Convert the characters of string text into key event's and send to - * device. - * - * @param text is a string of characters you want to input to the device. - */ - private void sendText(int source, String text) { + private int getDisplayId() { + String displayArg = nextArgRequired(); + if ("INVALID_DISPLAY".equalsIgnoreCase(displayArg)) { + return INVALID_DISPLAY; + } else if ("DEFAULT_DISPLAY".equalsIgnoreCase(displayArg)) { + return DEFAULT_DISPLAY; + } else { + try { + final int displayId = Integer.parseInt(displayArg); + if (displayId == INVALID_DISPLAY) { + return INVALID_DISPLAY; + } + return Math.max(displayId, 0); + } catch (NumberFormatException e) { + throw new IllegalArgumentException(INVALID_DISPLAY_ARGUMENTS); + } + } + } + + class InputText implements InputCmd { + @Override + public void run(int inputSource, int displayId) { + inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD); + sendText(inputSource, nextArgRequired(), displayId); + } - StringBuffer buff = new StringBuffer(text); + /** + * Convert the characters of string text into key event's and send to + * device. + * + * @param text is a string of characters you want to input to the device. + */ + private void sendText(int source, final String text, int displayId) { + final StringBuffer buff = new StringBuffer(text); + boolean escapeFlag = false; + for (int i = 0; i < buff.length(); i++) { + if (escapeFlag) { + escapeFlag = false; + if (buff.charAt(i) == 's') { + buff.setCharAt(i, ' '); + buff.deleteCharAt(--i); + } + } + if (buff.charAt(i) == '%') { + escapeFlag = true; + } + } - boolean escapeFlag = false; - for (int i=0; i<buff.length(); i++) { - if (escapeFlag) { - escapeFlag = false; - if (buff.charAt(i) == 's') { - buff.setCharAt(i, ' '); - buff.deleteCharAt(--i); + final char[] chars = buff.toString().toCharArray(); + final KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); + final KeyEvent[] events = kcm.getEvents(chars); + for (int i = 0; i < events.length; i++) { + KeyEvent e = events[i]; + if (source != e.getSource()) { + e.setSource(source); } + e.setDisplayId(displayId); + injectKeyEvent(e); } - if (buff.charAt(i) == '%') { - escapeFlag = true; + } + } + + class InputKeyEvent implements InputCmd { + @Override + public void run(int inputSource, int displayId) { + String arg = nextArgRequired(); + final boolean longpress = "--longpress".equals(arg); + if (longpress) { + arg = nextArgRequired(); } + + do { + final int keycode = KeyEvent.keyCodeFromString(arg); + sendKeyEvent(inputSource, keycode, longpress, displayId); + } while ((arg = nextArg()) != null); } - char[] chars = buff.toString().toCharArray(); + private void sendKeyEvent(int inputSource, int keyCode, boolean longpress, int displayId) { + final long now = SystemClock.uptimeMillis(); + int repeatCount = 0; + + KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, repeatCount, + 0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0/*flags*/, + inputSource); + event.setDisplayId(displayId); - KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); - KeyEvent[] events = kcm.getEvents(chars); - for(int i = 0; i < events.length; i++) { - KeyEvent e = events[i]; - if (source != e.getSource()) { - e.setSource(source); + injectKeyEvent(event); + if (longpress) { + repeatCount++; + injectKeyEvent(KeyEvent.changeTimeRepeat(event, now, repeatCount, + KeyEvent.FLAG_LONG_PRESS)); } - injectKeyEvent(e); + injectKeyEvent(KeyEvent.changeAction(event, KeyEvent.ACTION_UP)); } } - private void sendKeyEvent(int inputSource, int keyCode, boolean longpress) { - long now = SystemClock.uptimeMillis(); - injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0, 0, - KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource)); - if (longpress) { - injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 1, 0, - KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_LONG_PRESS, - inputSource)); + class InputTap implements InputCmd { + @Override + public void run(int inputSource, int displayId) { + inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN); + sendTap(inputSource, Float.parseFloat(nextArgRequired()), + Float.parseFloat(nextArgRequired()), displayId); + } + + void sendTap(int inputSource, float x, float y, int displayId) { + final long now = SystemClock.uptimeMillis(); + injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, now, x, y, 1.0f, + displayId); + injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, now, x, y, 0.0f, displayId); } - injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_UP, keyCode, 0, 0, - KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource)); } - private void sendTap(int inputSource, float x, float y) { - long now = SystemClock.uptimeMillis(); - injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x, y, 1.0f); - injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x, y, 0.0f); + class InputPress extends InputTap { + @Override + public void run(int inputSource, int displayId) { + inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL); + sendTap(inputSource, 0.0f, 0.0f, displayId); + } } - private void sendSwipe(int inputSource, float x1, float y1, float x2, float y2, int duration) { - if (duration < 0) { - duration = 300; + class InputSwipe implements InputCmd { + @Override + public void run(int inputSource, int displayId) { + inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN); + sendSwipe(inputSource, displayId, false); } - long now = SystemClock.uptimeMillis(); - injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x1, y1, 1.0f); - long startTime = now; - long endTime = startTime + duration; - while (now < endTime) { - long elapsedTime = now - startTime; - float alpha = (float) elapsedTime / duration; - injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, lerp(x1, x2, alpha), - lerp(y1, y2, alpha), 1.0f); - now = SystemClock.uptimeMillis(); + + void sendSwipe(int inputSource, int displayId, boolean isDragDrop) { + // Parse two points and duration. + final float x1 = Float.parseFloat(nextArgRequired()); + final float y1 = Float.parseFloat(nextArgRequired()); + final float x2 = Float.parseFloat(nextArgRequired()); + final float y2 = Float.parseFloat(nextArgRequired()); + String durationArg = nextArg(); + int duration = durationArg != null ? Integer.parseInt(durationArg) : -1; + if (duration < 0) { + duration = 300; + } + + final long down = SystemClock.uptimeMillis(); + injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, down, down, x1, y1, 1.0f, + displayId); + if (isDragDrop) { + // long press until drag start. + try { + Thread.sleep(ViewConfiguration.getLongPressTimeout()); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + long now = SystemClock.uptimeMillis(); + final long endTime = down + duration; + while (now < endTime) { + final long elapsedTime = now - down; + final float alpha = (float) elapsedTime / duration; + injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, down, now, + lerp(x1, x2, alpha), lerp(y1, y2, alpha), 1.0f, displayId); + now = SystemClock.uptimeMillis(); + } + injectMotionEvent(inputSource, MotionEvent.ACTION_UP, down, now, x2, y2, 0.0f, + displayId); } - injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x2, y2, 0.0f); } - private void sendDragAndDrop(int inputSource, float x1, float y1, float x2, float y2, - int dragDuration) { - if (dragDuration < 0) { - dragDuration = 300; + class InputDragAndDrop extends InputSwipe { + @Override + public void run(int inputSource, int displayId) { + inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN); + sendSwipe(inputSource, displayId, true); } - long now = SystemClock.uptimeMillis(); - injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x1, y1, 1.0f); - try { - Thread.sleep(ViewConfiguration.getLongPressTimeout()); - } catch (InterruptedException e) { - throw new RuntimeException(e); + } + + class InputRoll implements InputCmd { + @Override + public void run(int inputSource, int displayId) { + inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL); + sendMove(inputSource, Float.parseFloat(nextArgRequired()), + Float.parseFloat(nextArgRequired()), displayId); } - now = SystemClock.uptimeMillis(); - long startTime = now; - long endTime = startTime + dragDuration; - while (now < endTime) { - long elapsedTime = now - startTime; - float alpha = (float) elapsedTime / dragDuration; - injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, lerp(x1, x2, alpha), - lerp(y1, y2, alpha), 1.0f); - now = SystemClock.uptimeMillis(); + + /** + * Sends a simple zero-pressure move event. + * + * @param inputSource the InputDevice.SOURCE_* sending the input event + * @param dx change in x coordinate due to move + * @param dy change in y coordinate due to move + */ + private void sendMove(int inputSource, float dx, float dy, int displayId) { + final long now = SystemClock.uptimeMillis(); + injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, now, dx, dy, 0.0f, + displayId); } - injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x2, y2, 0.0f); } /** - * Sends a simple zero-pressure move event. - * - * @param inputSource the InputDevice.SOURCE_* sending the input event - * @param dx change in x coordinate due to move - * @param dy change in y coordinate due to move + * Abstract class for command + * use nextArgRequired or nextArg to check next argument if necessary. */ - private void sendMove(int inputSource, float dx, float dy) { - long now = SystemClock.uptimeMillis(); - injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, dx, dy, 0.0f); + private interface InputCmd { + void run(int inputSource, int displayId); } - private void injectKeyEvent(KeyEvent event) { - Log.i(TAG, "injectKeyEvent: " + event); + private static void injectKeyEvent(KeyEvent event) { InputManager.getInstance().injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH); } - private int getInputDeviceId(int inputSource) { + private static int getInputDeviceId(int inputSource) { final int DEFAULT_DEVICE_ID = 0; int[] devIds = InputDevice.getDeviceIds(); for (int devId : devIds) { @@ -287,22 +334,27 @@ public class Input { * * @param inputSource the InputDevice.SOURCE_* sending the input event * @param action the MotionEvent.ACTION_* for the event + * @param downTime the value of the ACTION_DOWN event happened * @param when the value of SystemClock.uptimeMillis() at which the event happened * @param x x coordinate of event * @param y y coordinate of event * @param pressure pressure of event */ - private void injectMotionEvent(int inputSource, int action, long when, float x, float y, float pressure) { + private static void injectMotionEvent(int inputSource, int action, long downTime, long when, + float x, float y, float pressure, int displayId) { final float DEFAULT_SIZE = 1.0f; final int DEFAULT_META_STATE = 0; final float DEFAULT_PRECISION_X = 1.0f; final float DEFAULT_PRECISION_Y = 1.0f; final int DEFAULT_EDGE_FLAGS = 0; - MotionEvent event = MotionEvent.obtain(when, when, action, x, y, pressure, DEFAULT_SIZE, + MotionEvent event = MotionEvent.obtain(downTime, when, action, x, y, pressure, DEFAULT_SIZE, DEFAULT_META_STATE, DEFAULT_PRECISION_X, DEFAULT_PRECISION_Y, getInputDeviceId(inputSource), DEFAULT_EDGE_FLAGS); event.setSource(inputSource); - Log.i(TAG, "injectMotionEvent: " + event); + if (displayId == INVALID_DISPLAY && (inputSource & InputDevice.SOURCE_CLASS_POINTER) != 0) { + displayId = DEFAULT_DISPLAY; + } + event.setDisplayId(displayId); InputManager.getInstance().injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH); } @@ -315,24 +367,29 @@ public class Input { return inputSource == InputDevice.SOURCE_UNKNOWN ? defaultSource : inputSource; } - private void showUsage() { - System.err.println("Usage: input [<source>] <command> [<arg>...]"); - System.err.println(); - System.err.println("The sources are: "); + @Override + public void onShowUsage(PrintStream out) { + out.println("Usage: input [<source>] [-d DISPLAY_ID] <command> [<arg>...]"); + out.println(); + out.println("The sources are: "); for (String src : SOURCES.keySet()) { - System.err.println(" " + src); + out.println(" " + src); } - System.err.println(); - System.err.println("The commands and default sources are:"); - System.err.println(" text <string> (Default: touchscreen)"); - System.err.println(" keyevent [--longpress] <key code number or name> ..." + out.println(); + out.printf("-d: specify the display ID.\n" + + " (Default: %d for key event, %d for motion event if not specified.)", + INVALID_DISPLAY, DEFAULT_DISPLAY); + out.println(); + out.println("The commands and default sources are:"); + out.println(" text <string> (Default: touchscreen)"); + out.println(" keyevent [--longpress] <key code number or name> ..." + " (Default: keyboard)"); - System.err.println(" tap <x> <y> (Default: touchscreen)"); - System.err.println(" swipe <x1> <y1> <x2> <y2> [duration(ms)]" + out.println(" tap <x> <y> (Default: touchscreen)"); + out.println(" swipe <x1> <y1> <x2> <y2> [duration(ms)]" + " (Default: touchscreen)"); - System.err.println(" draganddrop <x1> <y1> <x2> <y2> [duration(ms)]" + out.println(" draganddrop <x1> <y1> <x2> <y2> [duration(ms)]" + " (Default: touchscreen)"); - System.err.println(" press (Default: trackball)"); - System.err.println(" roll <dx> <dy> (Default: trackball)"); + out.println(" press (Default: trackball)"); + out.println(" roll <dx> <dy> (Default: trackball)"); } } diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java index c6d2bc891b18..6788f7d45071 100644 --- a/cmds/media/src/com/android/commands/media/Media.java +++ b/cmds/media/src/com/android/commands/media/Media.java @@ -24,7 +24,7 @@ import android.media.MediaMetadata; import android.media.session.ISessionController; import android.media.session.ISessionControllerCallback; import android.media.session.ISessionManager; -import android.media.session.ParcelableVolumeInfo; +import android.media.session.MediaController.PlaybackInfo; import android.media.session.PlaybackState; import android.os.Bundle; import android.os.HandlerThread; @@ -224,7 +224,7 @@ public class Media extends BaseCommand { } @Override - public void onVolumeInfoChanged(ParcelableVolumeInfo info) throws RemoteException { + public void onVolumeInfoChanged(PlaybackInfo info) throws RemoteException { System.out.println("onVolumeInfoChanged " + info); } diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt index d0ed28294288..e0e1b7202347 100644 --- a/config/hiddenapi-greylist.txt +++ b/config/hiddenapi-greylist.txt @@ -2767,92 +2767,6 @@ Lcom/android/internal/telephony/Connection;->mDuration:J Lcom/android/internal/telephony/Connection;->mIsIncoming:Z Lcom/android/internal/telephony/Connection;->mNumberPresentation:I Lcom/android/internal/telephony/Connection;->setVideoState(I)V -Lcom/android/internal/telephony/dataconnection/ApnContext;->getApnType()Ljava/lang/String; -Lcom/android/internal/telephony/dataconnection/ApnContext;->getReason()Ljava/lang/String; -Lcom/android/internal/telephony/dataconnection/ApnContext;->getState()Lcom/android/internal/telephony/DctConstants$State; -Lcom/android/internal/telephony/dataconnection/ApnContext;->isConnectable()Z -Lcom/android/internal/telephony/dataconnection/ApnContext;->isDisconnected()Z -Lcom/android/internal/telephony/dataconnection/ApnContext;->isEnabled()Z -Lcom/android/internal/telephony/dataconnection/ApnContext;->isReady()Z -Lcom/android/internal/telephony/dataconnection/ApnContext;->log(Ljava/lang/String;)V -Lcom/android/internal/telephony/dataconnection/ApnContext;->mApnType:Ljava/lang/String; -Lcom/android/internal/telephony/dataconnection/ApnContext;->mRefCount:I -Lcom/android/internal/telephony/dataconnection/ApnContext;->mRefCountLock:Ljava/lang/Object; -Lcom/android/internal/telephony/dataconnection/ApnContext;->setState(Lcom/android/internal/telephony/DctConstants$State;)V -Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;->mApnContext:Lcom/android/internal/telephony/dataconnection/ApnContext; -Lcom/android/internal/telephony/dataconnection/DataConnection;->clearSettings()V -Lcom/android/internal/telephony/dataconnection/DataConnection;->dumpToLog()V -Lcom/android/internal/telephony/dataconnection/DataConnection;->initConnection(Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;)Z -Lcom/android/internal/telephony/dataconnection/DataConnection;->log(Ljava/lang/String;)V -Lcom/android/internal/telephony/dataconnection/DataConnection;->mActivatingState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActivatingState; -Lcom/android/internal/telephony/dataconnection/DataConnection;->mActiveState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActiveState; -Lcom/android/internal/telephony/dataconnection/DataConnection;->mConnectionParams:Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams; -Lcom/android/internal/telephony/dataconnection/DataConnection;->mDataRegState:I -Lcom/android/internal/telephony/dataconnection/DataConnection;->mDct:Lcom/android/internal/telephony/dataconnection/DcTracker; -Lcom/android/internal/telephony/dataconnection/DataConnection;->mDisconnectingErrorCreatingConnection:Lcom/android/internal/telephony/dataconnection/DataConnection$DcDisconnectionErrorCreatingConnection; -Lcom/android/internal/telephony/dataconnection/DataConnection;->mDisconnectingState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcDisconnectingState; -Lcom/android/internal/telephony/dataconnection/DataConnection;->mDisconnectParams:Lcom/android/internal/telephony/dataconnection/DataConnection$DisconnectParams; -Lcom/android/internal/telephony/dataconnection/DataConnection;->mId:I -Lcom/android/internal/telephony/dataconnection/DataConnection;->mInactiveState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcInactiveState; -Lcom/android/internal/telephony/dataconnection/DataConnection;->mLinkProperties:Landroid/net/LinkProperties; -Lcom/android/internal/telephony/dataconnection/DataConnection;->mNetworkInfo:Landroid/net/NetworkInfo; -Lcom/android/internal/telephony/dataconnection/DataConnection;->mPhone:Lcom/android/internal/telephony/Phone; -Lcom/android/internal/telephony/dataconnection/DataConnection;->mRilRat:I -Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyAllOfConnected(Ljava/lang/String;)V -Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyDisconnectCompleted(Lcom/android/internal/telephony/dataconnection/DataConnection$DisconnectParams;Z)V -Lcom/android/internal/telephony/dataconnection/DataConnection;->onConnect(Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;)V -Lcom/android/internal/telephony/dataconnection/DataConnection;->tearDownData(Ljava/lang/Object;)V -Lcom/android/internal/telephony/dataconnection/DataConnection;->updateTcpBufferSizes(I)V -Lcom/android/internal/telephony/dataconnection/DcController;->lr(Ljava/lang/String;)V -Lcom/android/internal/telephony/dataconnection/DcController;->mDcListActiveByCid:Ljava/util/HashMap; -Lcom/android/internal/telephony/dataconnection/DcController;->mDct:Lcom/android/internal/telephony/dataconnection/DcTracker; -Lcom/android/internal/telephony/dataconnection/DcController;->mDcTesterDeactivateAll:Lcom/android/internal/telephony/dataconnection/DcTesterDeactivateAll; -Lcom/android/internal/telephony/dataconnection/DcTracker$RecoveryAction;->isAggressiveRecovery(I)Z -Lcom/android/internal/telephony/dataconnection/DcTracker;->cancelReconnectAlarm(Lcom/android/internal/telephony/dataconnection/ApnContext;)V -Lcom/android/internal/telephony/dataconnection/DcTracker;->cleanUpAllConnections(Ljava/lang/String;)V -Lcom/android/internal/telephony/dataconnection/DcTracker;->createAllApnList()V -Lcom/android/internal/telephony/dataconnection/DcTracker;->getActiveApnTypes()[Ljava/lang/String; -Lcom/android/internal/telephony/dataconnection/DcTracker;->getOverallState()Lcom/android/internal/telephony/DctConstants$State; -Lcom/android/internal/telephony/dataconnection/DcTracker;->getUiccRecords(I)Lcom/android/internal/telephony/uicc/IccRecords; -Lcom/android/internal/telephony/dataconnection/DcTracker;->isConnected()Z -Lcom/android/internal/telephony/dataconnection/DcTracker;->isDisconnected()Z -Lcom/android/internal/telephony/dataconnection/DcTracker;->isOnlySingleDcAllowed(I)Z -Lcom/android/internal/telephony/dataconnection/DcTracker;->log(Ljava/lang/String;)V -Lcom/android/internal/telephony/dataconnection/DcTracker;->loge(Ljava/lang/String;)V -Lcom/android/internal/telephony/dataconnection/DcTracker;->mAllApnSettings:Ljava/util/ArrayList; -Lcom/android/internal/telephony/dataconnection/DcTracker;->mApnContexts:Ljava/util/concurrent/ConcurrentHashMap; -Lcom/android/internal/telephony/dataconnection/DcTracker;->mAttached:Ljava/util/concurrent/atomic/AtomicBoolean; -Lcom/android/internal/telephony/dataconnection/DcTracker;->mAutoAttachOnCreation:Ljava/util/concurrent/atomic/AtomicBoolean; -Lcom/android/internal/telephony/dataconnection/DcTracker;->mDataConnectionTracker:Landroid/os/Handler; -Lcom/android/internal/telephony/dataconnection/DcTracker;->mDisconnectPendingCount:I -Lcom/android/internal/telephony/dataconnection/DcTracker;->mIccRecords:Ljava/util/concurrent/atomic/AtomicReference; -Lcom/android/internal/telephony/dataconnection/DcTracker;->mIsPsRestricted:Z -Lcom/android/internal/telephony/dataconnection/DcTracker;->mIsScreenOn:Z -Lcom/android/internal/telephony/dataconnection/DcTracker;->mNetStatPollEnabled:Z -Lcom/android/internal/telephony/dataconnection/DcTracker;->mNetStatPollPeriod:I -Lcom/android/internal/telephony/dataconnection/DcTracker;->mPhone:Lcom/android/internal/telephony/Phone; -Lcom/android/internal/telephony/dataconnection/DcTracker;->mPrioritySortedApnContexts:Ljava/util/PriorityQueue; -Lcom/android/internal/telephony/dataconnection/DcTracker;->mProvisioningSpinner:Landroid/app/ProgressDialog; -Lcom/android/internal/telephony/dataconnection/DcTracker;->mResolver:Landroid/content/ContentResolver; -Lcom/android/internal/telephony/dataconnection/DcTracker;->mState:Lcom/android/internal/telephony/DctConstants$State; -Lcom/android/internal/telephony/dataconnection/DcTracker;->mSubscriptionManager:Landroid/telephony/SubscriptionManager; -Lcom/android/internal/telephony/dataconnection/DcTracker;->onActionIntentDataStallAlarm(Landroid/content/Intent;)V -Lcom/android/internal/telephony/dataconnection/DcTracker;->onActionIntentProvisioningApnAlarm(Landroid/content/Intent;)V -Lcom/android/internal/telephony/dataconnection/DcTracker;->onRecordsLoadedOrSubIdChanged()V -Lcom/android/internal/telephony/dataconnection/DcTracker;->onTrySetupData(Lcom/android/internal/telephony/dataconnection/ApnContext;)Z -Lcom/android/internal/telephony/dataconnection/DcTracker;->onTrySetupData(Ljava/lang/String;)Z -Lcom/android/internal/telephony/dataconnection/DcTracker;->registerSettingsObserver()V -Lcom/android/internal/telephony/dataconnection/DcTracker;->resetPollStats()V -Lcom/android/internal/telephony/dataconnection/DcTracker;->restartDataStallAlarm()V -Lcom/android/internal/telephony/dataconnection/DcTracker;->setInitialAttachApn()V -Lcom/android/internal/telephony/dataconnection/DcTracker;->setPreferredApn(I)V -Lcom/android/internal/telephony/dataconnection/DcTracker;->setRadio(Z)V -Lcom/android/internal/telephony/dataconnection/DcTracker;->setupDataOnConnectableApns(Ljava/lang/String;)V -Lcom/android/internal/telephony/dataconnection/DcTracker;->startDataStallAlarm(Z)V -Lcom/android/internal/telephony/dataconnection/DcTracker;->startNetStatPoll()V -Lcom/android/internal/telephony/dataconnection/DcTracker;->stopDataStallAlarm()V -Lcom/android/internal/telephony/dataconnection/DcTracker;->stopNetStatPoll()V -Lcom/android/internal/telephony/dataconnection/DcTracker;->unregisterForAllDataDisconnected(Landroid/os/Handler;)V Lcom/android/internal/telephony/DctConstants$Activity;->DATAIN:Lcom/android/internal/telephony/DctConstants$Activity; Lcom/android/internal/telephony/DctConstants$Activity;->DATAINANDOUT:Lcom/android/internal/telephony/DctConstants$Activity; Lcom/android/internal/telephony/DctConstants$Activity;->DATAOUT:Lcom/android/internal/telephony/DctConstants$Activity; diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index 7cda8403f1f9..57132a72700d 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -321,7 +321,7 @@ public class ActivityView extends ViewGroup { public void surfaceCreated(SurfaceHolder surfaceHolder) { mTmpSurface = new Surface(); if (mVirtualDisplay == null) { - initVirtualDisplay(new SurfaceSession(surfaceHolder.getSurface())); + initVirtualDisplay(new SurfaceSession()); if (mVirtualDisplay != null && mActivityViewCallback != null) { mActivityViewCallback.onActivityViewReady(ActivityView.this); } @@ -389,6 +389,7 @@ public class ActivityView extends ViewGroup { mRootSurfaceControl = new SurfaceControl.Builder(surfaceSession) .setContainerLayer(true) + .setParent(mSurfaceView.getSurfaceControl()) .setName(DISPLAY_NAME) .build(); diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl index 3a2038d40952..666f7218a4f2 100644 --- a/core/java/android/app/IWallpaperManager.aidl +++ b/core/java/android/app/IWallpaperManager.aidl @@ -142,19 +142,20 @@ interface IWallpaperManager { * * @param which either {@link WallpaperManager#FLAG_LOCK} * or {@link WallpaperManager#FLAG_SYSTEM} + * @param displayId Which display is interested * @return colors of chosen wallpaper */ - WallpaperColors getWallpaperColors(int which, int userId); + WallpaperColors getWallpaperColors(int which, int userId, int displayId); /** - * Register a callback to receive color updates + * Register a callback to receive color updates from a display */ - void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId); + void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId, int displayId); /** - * Unregister a callback that was receiving color updates + * Unregister a callback that was receiving color updates from a display */ - void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId); + void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId, int displayId); /** * Called from SystemUI when it shows the AoD UI. diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 27ae0b0314d0..a929fe0f688e 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -307,13 +307,14 @@ public class WallpaperManager { * @param callback Listener * @param handler Thread to call it from. Main thread if null. * @param userId Owner of the wallpaper or UserHandle.USER_ALL + * @param displayId Caller comes from which display */ public void addOnColorsChangedListener(@NonNull OnColorsChangedListener callback, - @Nullable Handler handler, int userId) { + @Nullable Handler handler, int userId, int displayId) { synchronized (this) { if (!mColorCallbackRegistered) { try { - mService.registerWallpaperColorsCallback(this, userId); + mService.registerWallpaperColorsCallback(this, userId, displayId); mColorCallbackRegistered = true; } catch (RemoteException e) { // Failed, service is gone @@ -329,16 +330,17 @@ public class WallpaperManager { * * @param callback listener * @param userId Owner of the wallpaper or UserHandle.USER_ALL + * @param displayId Which display is interested */ public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener callback, - int userId) { + int userId, int displayId) { synchronized (this) { mColorListeners.removeIf(pair -> pair.first == callback); if (mColorListeners.size() == 0 && mColorCallbackRegistered) { mColorCallbackRegistered = false; try { - mService.unregisterWallpaperColorsCallback(this, userId); + mService.unregisterWallpaperColorsCallback(this, userId, displayId); } catch (RemoteException e) { // Failed, service is gone Log.w(TAG, "Can't unregister color updates", e); @@ -370,14 +372,14 @@ public class WallpaperManager { } } - WallpaperColors getWallpaperColors(int which, int userId) { + WallpaperColors getWallpaperColors(int which, int userId, int displayId) { if (which != FLAG_LOCK && which != FLAG_SYSTEM) { throw new IllegalArgumentException( "Must request colors for exactly one kind of wallpaper"); } try { - return mService.getWallpaperColors(which, userId); + return mService.getWallpaperColors(which, userId, displayId); } catch (RemoteException e) { // Can't get colors, connection lost. } @@ -894,7 +896,7 @@ public class WallpaperManager { @UnsupportedAppUsage public void addOnColorsChangedListener(@NonNull OnColorsChangedListener listener, @NonNull Handler handler, int userId) { - sGlobals.addOnColorsChangedListener(listener, handler, userId); + sGlobals.addOnColorsChangedListener(listener, handler, userId, mContext.getDisplayId()); } /** @@ -913,7 +915,7 @@ public class WallpaperManager { */ public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener callback, int userId) { - sGlobals.removeOnColorsChangedListener(callback, userId); + sGlobals.removeOnColorsChangedListener(callback, userId, mContext.getDisplayId()); } /** @@ -947,7 +949,7 @@ public class WallpaperManager { */ @UnsupportedAppUsage public @Nullable WallpaperColors getWallpaperColors(int which, int userId) { - return sGlobals.getWallpaperColors(which, userId); + return sGlobals.getWallpaperColors(which, userId, mContext.getDisplayId()); } /** diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl index 4d52263c1d78..bbae7d3463ae 100644 --- a/core/java/android/app/usage/IUsageStatsManager.aidl +++ b/core/java/android/app/usage/IUsageStatsManager.aidl @@ -55,4 +55,8 @@ interface IUsageStatsManager { long sessionThresholdTimeMs, in PendingIntent limitReachedCallbackIntent, in PendingIntent sessionEndCallbackIntent, String callingPackage); void unregisterUsageSessionObserver(int sessionObserverId, String callingPackage); + void reportUsageStart(in IBinder activity, String token, String callingPackage); + void reportPastUsageStart(in IBinder activity, String token, long timeAgoMs, + String callingPackage); + void reportUsageStop(in IBinder activity, String token, String callingPackage); } diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index 26beb45be13f..605deac80994 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -23,6 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.UnsupportedAppUsage; +import android.app.Activity; import android.app.PendingIntent; import android.content.Context; import android.content.pm.ParceledListSlice; @@ -579,15 +580,18 @@ public final class UsageStatsManager { /** * @hide * Register an app usage limit observer that receives a callback on the provided intent when - * the sum of usages of apps in the packages array exceeds the {@code timeLimit} specified. The - * observer will automatically be unregistered when the time limit is reached and the intent - * is delivered. Registering an {@code observerId} that was already registered will override - * the previous one. No more than 1000 unique {@code observerId} may be registered by a single - * uid at any one time. + * the sum of usages of apps and tokens in the {@code observed} array exceeds the + * {@code timeLimit} specified. The structure of a token is a String with the reporting + * package's name and a token the reporting app will use, separated by the forward slash + * character. Example: com.reporting.package/5OM3*0P4QU3-7OK3N + * The observer will automatically be unregistered when the time limit is reached and the + * intent is delivered. Registering an {@code observerId} that was already registered will + * override the previous one. No more than 1000 unique {@code observerId} may be registered by + * a single uid at any one time. * @param observerId A unique id associated with the group of apps to be monitored. There can * be multiple groups with common packages and different time limits. - * @param packages The list of packages to observe for foreground activity time. Cannot be null - * and must include at least one package. + * @param observedEntities The list of packages and token to observe for usage time. Cannot be + * null and must include at least one package or token. * @param timeLimit The total time the set of apps can be in the foreground before the * callbackIntent is delivered. Must be at least one minute. * @param timeUnit The unit for time specified in {@code timeLimit}. Cannot be null. @@ -600,11 +604,11 @@ public final class UsageStatsManager { */ @SystemApi @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) - public void registerAppUsageObserver(int observerId, @NonNull String[] packages, long timeLimit, - @NonNull TimeUnit timeUnit, @NonNull PendingIntent callbackIntent) { + public void registerAppUsageObserver(int observerId, @NonNull String[] observedEntities, + long timeLimit, @NonNull TimeUnit timeUnit, @NonNull PendingIntent callbackIntent) { try { - mService.registerAppUsageObserver(observerId, packages, timeUnit.toMillis(timeLimit), - callbackIntent, mContext.getOpPackageName()); + mService.registerAppUsageObserver(observerId, observedEntities, + timeUnit.toMillis(timeLimit), callbackIntent, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -631,18 +635,21 @@ public final class UsageStatsManager { /** * Register a usage session observer that receives a callback on the provided {@code - * limitReachedCallbackIntent} when the sum of usages of apps in the packages array exceeds - * the {@code timeLimit} specified within a usage session. After the {@code timeLimit} has - * been reached, the usage session observer will receive a callback on the provided {@code - * sessionEndCallbackIntent} when the usage session ends. Registering another session - * observer against a {@code sessionObserverId} that has already been registered will - * override the previous session observer. + * limitReachedCallbackIntent} when the sum of usages of apps and tokens in the {@code + * observed} array exceeds the {@code timeLimit} specified within a usage session. The + * structure of a token is a String with the reporting packages' name and a token the + * reporting app will use, separated by the forward slash character. + * Example: com.reporting.package/5OM3*0P4QU3-7OK3N + * After the {@code timeLimit} has been reached, the usage session observer will receive a + * callback on the provided {@code sessionEndCallbackIntent} when the usage session ends. + * Registering another session observer against a {@code sessionObserverId} that has already + * been registered will override the previous session observer. * * @param sessionObserverId A unique id associated with the group of apps to be * monitored. There can be multiple groups with common * packages and different time limits. - * @param packages The list of packages to observe for foreground activity time. Cannot be null - * and must include at least one package. + * @param observedEntities The list of packages and token to observe for usage time. Cannot be + * null and must include at least one package or token. * @param timeLimit The total time the set of apps can be used continuously before the {@code * limitReachedCallbackIntent} is delivered. Must be at least one minute. * @param timeUnit The unit for time specified in {@code timeLimit}. Cannot be null. @@ -668,13 +675,13 @@ public final class UsageStatsManager { */ @SystemApi @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) - public void registerUsageSessionObserver(int sessionObserverId, @NonNull String[] packages, - long timeLimit, @NonNull TimeUnit timeUnit, long sessionThresholdTime, - @NonNull TimeUnit sessionThresholdTimeUnit, + public void registerUsageSessionObserver(int sessionObserverId, + @NonNull String[] observedEntities, long timeLimit, @NonNull TimeUnit timeUnit, + long sessionThresholdTime, @NonNull TimeUnit sessionThresholdTimeUnit, @NonNull PendingIntent limitReachedCallbackIntent, @Nullable PendingIntent sessionEndCallbackIntent) { try { - mService.registerUsageSessionObserver(sessionObserverId, packages, + mService.registerUsageSessionObserver(sessionObserverId, observedEntities, timeUnit.toMillis(timeLimit), sessionThresholdTimeUnit.toMillis(sessionThresholdTime), limitReachedCallbackIntent, sessionEndCallbackIntent, @@ -704,6 +711,71 @@ public final class UsageStatsManager { } } + /** + * Report usage associated with a particular {@code token} has started. Tokens are app defined + * strings used to represent usage of in-app features. Apps with the {@link + * android.Manifest.permission#OBSERVE_APP_USAGE} permission can register time limit observers + * to monitor the usage of a token. In app usage can only associated with an {@code activity} + * and usage will be considered stopped if the activity stops or crashes. + * @see #registerAppUsageObserver + * @see #registerUsageSessionObserver + * + * @param activity The activity {@code token} is associated with. + * @param token The token to report usage against. + * @hide + */ + @SystemApi + public void reportUsageStart(@NonNull Activity activity, @NonNull String token) { + try { + mService.reportUsageStart(activity.getActivityToken(), token, + mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Report usage associated with a particular {@code token} had started some amount of time in + * the past. Tokens are app defined strings used to represent usage of in-app features. Apps + * with the {@link android.Manifest.permission#OBSERVE_APP_USAGE} permission can register time + * limit observers to monitor the usage of a token. In app usage can only associated with an + * {@code activity} and usage will be considered stopped if the activity stops or crashes. + * @see #registerAppUsageObserver + * @see #registerUsageSessionObserver + * + * @param activity The activity {@code token} is associated with. + * @param token The token to report usage against. + * @param timeAgoMs How long ago the start of usage took place + * @hide + */ + @SystemApi + public void reportUsageStart(@NonNull Activity activity, @NonNull String token, + long timeAgoMs) { + try { + mService.reportPastUsageStart(activity.getActivityToken(), token, timeAgoMs, + mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Report the usage associated with a particular {@code token} has stopped. + * + * @param activity The activity {@code token} is associated with. + * @param token The token to report usage against. + * @hide + */ + @SystemApi + public void reportUsageStop(@NonNull Activity activity, @NonNull String token) { + try { + mService.reportUsageStop(activity.getActivityToken(), token, + mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** @hide */ public static String reasonToString(int standbyReason) { StringBuilder sb = new StringBuilder(); diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 7b3497b8b483..ab60b845e6fe 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -816,6 +816,28 @@ public class Intent implements Parcelable, Cloneable { = "android.intent.action.SHOW_APP_INFO"; /** + * Activity Action: Start an activity to show the app's detailed usage information for + * permission protected data. + * + * The Intent contains an extra {@link #EXTRA_PERMISSION_USAGE_PERMISSIONS} that is of + * type {@code String[]} and contains the specific permissions to show information for. + * + * Apps should handle this intent if they want to provide more information about permission + * usage to users beyond the information provided in the manifest. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_PERMISSION_USAGE_DETAILS = + "android.intent.action.PERMISSION_USAGE_DETAILS"; + + /** + * The name of the extra used to contain the permissions in + * {@link #ACTION_PERMISSION_USAGE_DETAILS}. + * @see #ACTION_PERMISSION_USAGE_DETAILS + */ + public static final String EXTRA_PERMISSION_USAGE_PERMISSIONS = + "android.intent.extra.PERMISSION_USAGE_PERMISSIONS"; + + /** * Represents a shortcut/live folder icon resource. * * @see Intent#ACTION_CREATE_SHORTCUT diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 61d5a9127743..23e4ec0567f2 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -16,6 +16,7 @@ package android.net; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; @@ -909,6 +910,7 @@ public class ConnectivityManager { */ @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) + @Nullable public NetworkInfo getActiveNetworkInfo() { try { return mService.getActiveNetworkInfo(); @@ -928,6 +930,7 @@ public class ConnectivityManager { * {@code null} if no default network is currently active */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) + @Nullable public Network getActiveNetwork() { try { return mService.getActiveNetwork(); @@ -949,6 +952,7 @@ public class ConnectivityManager { * @hide */ @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) + @Nullable public Network getActiveNetworkForUid(int uid) { return getActiveNetworkForUid(uid, false); } @@ -1074,6 +1078,7 @@ public class ConnectivityManager { */ @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) + @Nullable public NetworkInfo getNetworkInfo(int networkType) { try { return mService.getNetworkInfo(networkType); @@ -1095,7 +1100,8 @@ public class ConnectivityManager { */ @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) - public NetworkInfo getNetworkInfo(Network network) { + @Nullable + public NetworkInfo getNetworkInfo(@Nullable Network network) { return getNetworkInfoForUid(network, Process.myUid(), false); } @@ -1121,6 +1127,7 @@ public class ConnectivityManager { */ @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) + @NonNull public NetworkInfo[] getAllNetworkInfo() { try { return mService.getAllNetworkInfo(); @@ -1156,6 +1163,7 @@ public class ConnectivityManager { * @return an array of {@link Network} objects. */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) + @NonNull public Network[] getAllNetworks() { try { return mService.getAllNetworks(); @@ -1230,7 +1238,8 @@ public class ConnectivityManager { * @return The {@link LinkProperties} for the network, or {@code null}. */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) - public LinkProperties getLinkProperties(Network network) { + @Nullable + public LinkProperties getLinkProperties(@Nullable Network network) { try { return mService.getLinkProperties(network); } catch (RemoteException e) { @@ -1246,7 +1255,8 @@ public class ConnectivityManager { * @return The {@link android.net.NetworkCapabilities} for the network, or {@code null}. */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) - public NetworkCapabilities getNetworkCapabilities(Network network) { + @Nullable + public NetworkCapabilities getNetworkCapabilities(@Nullable Network network) { try { return mService.getNetworkCapabilities(network); } catch (RemoteException e) { @@ -2000,7 +2010,7 @@ public class ConnectivityManager { * * @param l Previously registered listener. */ - public void removeDefaultNetworkActiveListener(OnNetworkActiveListener l) { + public void removeDefaultNetworkActiveListener(@NonNull OnNetworkActiveListener l) { INetworkActivityListener rl = mNetworkActivityListeners.get(l); Preconditions.checkArgument(rl != null, "Listener was not registered."); try { @@ -2528,7 +2538,7 @@ public class ConnectivityManager { * working and non-working connectivity. */ @Deprecated - public void reportBadNetwork(Network network) { + public void reportBadNetwork(@Nullable Network network) { printStackTrace(); try { // One of these will be ignored because it matches system's current state. @@ -2551,7 +2561,7 @@ public class ConnectivityManager { * @param hasConnectivity {@code true} if the application was able to successfully access the * Internet using {@code network} or {@code false} if not. */ - public void reportNetworkConnectivity(Network network, boolean hasConnectivity) { + public void reportNetworkConnectivity(@Nullable Network network, boolean hasConnectivity) { printStackTrace(); try { mService.reportNetworkConnectivity(network, hasConnectivity); @@ -2625,6 +2635,7 @@ public class ConnectivityManager { * @return the {@link ProxyInfo} for the current HTTP proxy, or {@code null} if no * HTTP proxy is active. */ + @Nullable public ProxyInfo getDefaultProxy() { return getProxyForNetwork(getBoundNetworkForProcess()); } @@ -3156,8 +3167,9 @@ public class ConnectivityManager { * * @hide */ - public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback, - int timeoutMs, int legacyType, Handler handler) { + public void requestNetwork(@NonNull NetworkRequest request, + @NonNull NetworkCallback networkCallback, int timeoutMs, int legacyType, + @NonNull Handler handler) { CallbackHandler cbHandler = new CallbackHandler(handler); NetworkCapabilities nc = request.networkCapabilities; sendRequestForNetwork(nc, networkCallback, timeoutMs, REQUEST, legacyType, cbHandler); @@ -3194,7 +3206,8 @@ public class ConnectivityManager { * @throws IllegalArgumentException if {@code request} specifies any mutable * {@code NetworkCapabilities}. */ - public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback) { + public void requestNetwork(@NonNull NetworkRequest request, + @NonNull NetworkCallback networkCallback) { requestNetwork(request, networkCallback, getDefaultHandler()); } @@ -3229,8 +3242,8 @@ public class ConnectivityManager { * @throws IllegalArgumentException if {@code request} specifies any mutable * {@code NetworkCapabilities}. */ - public void requestNetwork( - NetworkRequest request, NetworkCallback networkCallback, Handler handler) { + public void requestNetwork(@NonNull NetworkRequest request, + @NonNull NetworkCallback networkCallback, @NonNull Handler handler) { int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities); CallbackHandler cbHandler = new CallbackHandler(handler); requestNetwork(request, networkCallback, 0, legacyType, cbHandler); @@ -3264,8 +3277,8 @@ public class ConnectivityManager { * before {@link NetworkCallback#onUnavailable()} is called. The timeout must * be a positive value (i.e. >0). */ - public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback, - int timeoutMs) { + public void requestNetwork(@NonNull NetworkRequest request, + @NonNull NetworkCallback networkCallback, int timeoutMs) { checkTimeout(timeoutMs); int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities); requestNetwork(request, networkCallback, timeoutMs, legacyType, getDefaultHandler()); @@ -3298,8 +3311,8 @@ public class ConnectivityManager { * @param timeoutMs The time in milliseconds to attempt looking for a suitable network * before {@link NetworkCallback#onUnavailable} is called. */ - public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback, - Handler handler, int timeoutMs) { + public void requestNetwork(@NonNull NetworkRequest request, + @NonNull NetworkCallback networkCallback, @NonNull Handler handler, int timeoutMs) { checkTimeout(timeoutMs); int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities); CallbackHandler cbHandler = new CallbackHandler(handler); @@ -3371,7 +3384,8 @@ public class ConnectivityManager { * {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or * {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL}. */ - public void requestNetwork(NetworkRequest request, PendingIntent operation) { + public void requestNetwork(@NonNull NetworkRequest request, + @NonNull PendingIntent operation) { printStackTrace(); checkPendingIntentNotNull(operation); try { @@ -3395,7 +3409,7 @@ public class ConnectivityManager { * {@link #requestNetwork(NetworkRequest, android.app.PendingIntent)} with the * corresponding NetworkRequest you'd like to remove. Cannot be null. */ - public void releaseNetworkRequest(PendingIntent operation) { + public void releaseNetworkRequest(@NonNull PendingIntent operation) { printStackTrace(); checkPendingIntentNotNull(operation); try { @@ -3428,7 +3442,8 @@ public class ConnectivityManager { * The callback is invoked on the default internal Handler. */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) - public void registerNetworkCallback(NetworkRequest request, NetworkCallback networkCallback) { + public void registerNetworkCallback(@NonNull NetworkRequest request, + @NonNull NetworkCallback networkCallback) { registerNetworkCallback(request, networkCallback, getDefaultHandler()); } @@ -3443,8 +3458,8 @@ public class ConnectivityManager { * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) - public void registerNetworkCallback( - NetworkRequest request, NetworkCallback networkCallback, Handler handler) { + public void registerNetworkCallback(@NonNull NetworkRequest request, + @NonNull NetworkCallback networkCallback, @NonNull Handler handler) { CallbackHandler cbHandler = new CallbackHandler(handler); NetworkCapabilities nc = request.networkCapabilities; sendRequestForNetwork(nc, networkCallback, 0, LISTEN, TYPE_NONE, cbHandler); @@ -3480,7 +3495,8 @@ public class ConnectivityManager { * comes from {@link PendingIntent#getBroadcast}. Cannot be null. */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) - public void registerNetworkCallback(NetworkRequest request, PendingIntent operation) { + public void registerNetworkCallback(@NonNull NetworkRequest request, + @NonNull PendingIntent operation) { printStackTrace(); checkPendingIntentNotNull(operation); try { @@ -3502,7 +3518,7 @@ public class ConnectivityManager { * The callback is invoked on the default internal Handler. */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) - public void registerDefaultNetworkCallback(NetworkCallback networkCallback) { + public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback) { registerDefaultNetworkCallback(networkCallback, getDefaultHandler()); } @@ -3516,7 +3532,8 @@ public class ConnectivityManager { * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) - public void registerDefaultNetworkCallback(NetworkCallback networkCallback, Handler handler) { + public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback, + @NonNull Handler handler) { // This works because if the NetworkCapabilities are null, // ConnectivityService takes them from the default request. // @@ -3541,7 +3558,7 @@ public class ConnectivityManager { * @param network {@link Network} specifying which network you're interested. * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid. */ - public boolean requestBandwidthUpdate(Network network) { + public boolean requestBandwidthUpdate(@NonNull Network network) { try { return mService.requestBandwidthUpdate(network); } catch (RemoteException e) { @@ -3562,7 +3579,7 @@ public class ConnectivityManager { * * @param networkCallback The {@link NetworkCallback} used when making the request. */ - public void unregisterNetworkCallback(NetworkCallback networkCallback) { + public void unregisterNetworkCallback(@NonNull NetworkCallback networkCallback) { printStackTrace(); checkCallbackNotNull(networkCallback); final List<NetworkRequest> reqs = new ArrayList<>(); @@ -3601,7 +3618,7 @@ public class ConnectivityManager { * {@link #registerNetworkCallback(NetworkRequest, android.app.PendingIntent)}. * Cannot be null. */ - public void unregisterNetworkCallback(PendingIntent operation) { + public void unregisterNetworkCallback(@NonNull PendingIntent operation) { checkPendingIntentNotNull(operation); releaseNetworkRequest(operation); } @@ -3723,7 +3740,7 @@ public class ConnectivityManager { * @return a bitwise OR of zero or more of the {@code MULTIPATH_PREFERENCE_*} constants. */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) - public @MultipathPreference int getMultipathPreference(Network network) { + public @MultipathPreference int getMultipathPreference(@Nullable Network network) { try { return mService.getMultipathPreference(network); } catch (RemoteException e) { @@ -3761,7 +3778,7 @@ public class ConnectivityManager { * the current binding. * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid. */ - public boolean bindProcessToNetwork(Network network) { + public boolean bindProcessToNetwork(@Nullable Network network) { // Forcing callers to call through non-static function ensures ConnectivityManager // instantiated. return setProcessDefaultNetwork(network); @@ -3789,7 +3806,7 @@ public class ConnectivityManager { * is a direct replacement. */ @Deprecated - public static boolean setProcessDefaultNetwork(Network network) { + public static boolean setProcessDefaultNetwork(@Nullable Network network) { int netId = (network == null) ? NETID_UNSET : network.netId; if (netId == NetworkUtils.getBoundNetworkForProcess()) { return true; @@ -3820,6 +3837,7 @@ public class ConnectivityManager { * * @return {@code Network} to which this process is bound, or {@code null}. */ + @Nullable public Network getBoundNetworkForProcess() { // Forcing callers to call thru non-static function ensures ConnectivityManager // instantiated. @@ -3836,6 +3854,7 @@ public class ConnectivityManager { * {@code getBoundNetworkForProcess} is a direct replacement. */ @Deprecated + @Nullable public static Network getProcessDefaultNetwork() { int netId = NetworkUtils.getBoundNetworkForProcess(); if (netId == NETID_UNSET) return null; @@ -3962,6 +3981,7 @@ public class ConnectivityManager { * * @return Hash of network watchlist config file. Null if config does not exist. */ + @Nullable public byte[] getNetworkWatchlistConfigHash() { try { return mService.getNetworkWatchlistConfigHash(); @@ -3983,8 +4003,8 @@ public class ConnectivityManager { * (e.g., if it is associated with the calling VPN app's tunnel) or * {@link android.os.Process#INVALID_UID} if the connection is not found. */ - public int getConnectionOwnerUid(int protocol, InetSocketAddress local, - InetSocketAddress remote) { + public int getConnectionOwnerUid(int protocol, @NonNull InetSocketAddress local, + @NonNull InetSocketAddress remote) { ConnectionInfo connectionInfo = new ConnectionInfo(protocol, local, remote); try { return mService.getConnectionOwnerUid(connectionInfo); diff --git a/core/java/android/net/dhcp/DhcpServingParamsParcel.aidl b/core/java/android/net/dhcp/DhcpServingParamsParcel.aidl new file mode 100644 index 000000000000..7b8b9ee324bc --- /dev/null +++ b/core/java/android/net/dhcp/DhcpServingParamsParcel.aidl @@ -0,0 +1,30 @@ +/** + * + * Copyright (C) 2018 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.net.dhcp; + +parcelable DhcpServingParamsParcel { + int serverAddr; + int serverAddrPrefixLength; + int[] defaultRouters; + int[] dnsServers; + int[] excludedAddrs; + long dhcpLeaseTimeSecs; + int linkMtu; + boolean metered; +} + diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index d9793097fc3a..4c0ee6fcaa43 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -353,26 +353,6 @@ public class GraphicsEnvironment { } /** - * Attempt to setup ANGLE with a (temporary) default rules file: b/121153494 - * True: Rules file was loaded. - * False: Rules file was *not* loaded. - */ - private boolean setupAngleRulesDebug(String packageName, String paths, String devOptIn) { - // b/121153494 - // Skip APK rules file checking. - if (!DEBUG) { - Log.v(TAG, "Skipping loading the rules file."); - // Fill in some default values for now, so the loader can get an answer when it asks. - // Most importantly, we need to indicate which app we are init'ing and what the - // developer options for it are so we can turn on ANGLE if needed. - setAngleInfo(paths, packageName, devOptIn, null, 0, 0); - return true; - } - - return false; - } - - /** * Attempt to setup ANGLE with a rules file loaded from the ANGLE APK. * True: APK rules file was loaded. * False: APK rules file was *not* loaded. @@ -450,12 +430,6 @@ public class GraphicsEnvironment { return; } - // b/121153494 - if (setupAngleRulesDebug(packageName, paths, devOptIn)) { - // We setup ANGLE with defaults, so we're done here. - return; - } - if (setupAngleRulesApk(anglePkgName, angleInfo, context, packageName, paths, devOptIn)) { // We setup ANGLE with rules from the APK, so we're done here. return; diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index 6ea155fe70f8..980140675959 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -95,6 +95,8 @@ public final class Trace { public static final long TRACE_TAG_AIDL = 1L << 24; /** @hide */ public static final long TRACE_TAG_NNAPI = 1L << 25; + /** @hide */ + public static final long TRACE_TAG_RRO = 1L << 26; private static final long TRACE_TAG_NOT_READY = 1L << 63; private static final int MAX_SECTION_NAME_LEN = 127; diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java index 8f2826c16b63..1df3dad9530b 100644 --- a/core/java/android/os/UpdateEngine.java +++ b/core/java/android/os/UpdateEngine.java @@ -66,6 +66,7 @@ public class UpdateEngine { public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10; public static final int PAYLOAD_SIZE_MISMATCH_ERROR = 11; public static final int DOWNLOAD_PAYLOAD_VERIFICATION_ERROR = 12; + public static final int PAYLOAD_TIMESTAMP_ERROR = 51; public static final int UPDATED_BUT_NOT_ACTIVE = 52; } diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl index 38951d5466c7..0e18b445fd01 100644 --- a/core/java/android/permission/IPermissionController.aidl +++ b/core/java/android/permission/IPermissionController.aidl @@ -17,6 +17,7 @@ package android.permission; import android.os.RemoteCallback; +import android.os.Bundle; /** * Interface for system apps to communication with the permission controller. @@ -24,6 +25,8 @@ import android.os.RemoteCallback; * @hide */ oneway interface IPermissionController { + void revokeRuntimePermissions(in Bundle request, boolean doDryRun, int reason, + String callerPackageName, in RemoteCallback callback); void getAppPermissions(String packageName, in RemoteCallback callback); void revokeRuntimePermission(String packageName, String permissionName); void countPermissionApps(in List<String> permissionNames, boolean countOnlyGranted, diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java index 66e8666a8a70..e21a6608bee0 100644 --- a/core/java/android/permission/PermissionControllerManager.java +++ b/core/java/android/permission/PermissionControllerManager.java @@ -22,46 +22,97 @@ import static com.android.internal.util.Preconditions.checkCollectionElementsNot import static com.android.internal.util.Preconditions.checkNotNull; import android.Manifest; +import android.annotation.CallbackExecutor; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.annotation.SystemService; +import android.annotation.TestApi; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.os.Binder; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.UserHandle; +import android.util.ArrayMap; import android.util.Log; import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService; import com.android.internal.infra.AbstractRemoteService; +import com.android.internal.util.Preconditions; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; /** - * Interface for communicating with the permission controller from system apps. All UI operations - * regarding permissions and any changes to the permission state should flow through this - * interface. + * Interface for communicating with the permission controller. * * @hide */ +@TestApi +@SystemApi @SystemService(Context.PERMISSION_CONTROLLER_SERVICE) public final class PermissionControllerManager { private static final String TAG = PermissionControllerManager.class.getSimpleName(); /** * The key for retrieving the result from the returned bundle. + * + * @hide */ public static final String KEY_RESULT = "android.permission.PermissionControllerManager.key.result"; + /** @hide */ + @IntDef(prefix = { "REASON_" }, value = { + REASON_MALWARE, + REASON_INSTALLER_POLICY_VIOLATION, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Reason {} + + /** The permissions are revoked because the apps holding the permissions are malware */ + public static final int REASON_MALWARE = 1; + + /** + * The permissions are revoked because the apps holding the permissions violate a policy of the + * app that installed it. + * + * <p>If this reason is used only permissions of apps that are installed by the caller of the + * API can be revoked. + */ + public static final int REASON_INSTALLER_POLICY_VIOLATION = 2; + + /** + * Callback for delivering the result of {@link #revokeRuntimePermissions}. + */ + public abstract static class OnRevokeRuntimePermissionsCallback { + /** + * The result for {@link #revokeRuntimePermissions}. + * + * @param revoked The actually revoked permissions as + * {@code Map<packageName, List<permission>>} + */ + public abstract void onRevokeRuntimePermissions(@NonNull Map<String, List<String>> revoked); + } + /** * Callback for delivering the result of {@link #getAppPermissions}. + * + * @hide */ public interface OnGetAppPermissionResultCallback { /** @@ -75,6 +126,8 @@ public final class PermissionControllerManager { /** * Callback for delivering the result of {@link #countPermissionApps}. + * + * @hide */ public interface OnCountPermissionAppsResultCallback { /** @@ -86,23 +139,61 @@ public final class PermissionControllerManager { void onCountPermissionApps(int numApps); } + private final @NonNull Context mContext; private final RemoteService mRemoteService; + /** @hide */ public PermissionControllerManager(@NonNull Context context) { Intent intent = new Intent(SERVICE_INTERFACE); intent.setPackage(context.getPackageManager().getPermissionControllerPackageName()); ResolveInfo serviceInfo = context.getPackageManager().resolveService(intent, 0); + mContext = context; mRemoteService = new RemoteService(context, serviceInfo.getComponentInfo().getComponentName()); } /** + * Revoke a set of runtime permissions for various apps. + * + * @param request The permissions to revoke as {@code Map<packageName, List<permission>>} + * @param doDryRun Compute the permissions that would be revoked, but not actually revoke them + * @param reason Why the permission should be revoked + * @param executor Executor on which to invoke the callback + * @param callback Callback to receive the result + */ + @RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) + public void revokeRuntimePermissions(@NonNull Map<String, List<String>> request, + boolean doDryRun, @Reason int reason, @NonNull @CallbackExecutor Executor executor, + @NonNull OnRevokeRuntimePermissionsCallback callback) { + // Check input to fail immediately instead of inside the async request + checkNotNull(executor); + checkNotNull(callback); + checkNotNull(request); + for (Map.Entry<String, List<String>> appRequest : request.entrySet()) { + checkNotNull(appRequest.getKey()); + checkCollectionElementsNotNull(appRequest.getValue(), "permissions"); + } + + // Check required permission to fail immediately instead of inside the oneway binder call + if (mContext.checkSelfPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS + + " required"); + } + + mRemoteService.scheduleRequest(new PendingRevokeRuntimePermissionRequest(mRemoteService, + request, doDryRun, reason, mContext.getPackageName(), executor, callback)); + } + + /** * Gets the runtime permissions for an app. * * @param packageName The package for which to query. * @param callback Callback to receive the result. * @param handler Handler on which to invoke the callback. + * + * @hide */ @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS) public void getAppPermissions(@NonNull String packageName, @@ -119,6 +210,8 @@ public final class PermissionControllerManager { * * @param packageName The package for which to revoke * @param permissionName The permission to revoke + * + * @hide */ @RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public void revokeRuntimePermission(@NonNull String packageName, @@ -138,6 +231,8 @@ public final class PermissionControllerManager { * @param countSystem Also count system apps * @param callback Callback to receive the result * @param handler Handler on which to invoke the callback + * + * @hide */ @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS) public void countPermissionApps(@NonNull List<String> permissionNames, @@ -206,6 +301,84 @@ public final class PermissionControllerManager { } /** + * Request for {@link #revokeRuntimePermissions} + */ + private static final class PendingRevokeRuntimePermissionRequest extends + AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> { + private final @NonNull Map<String, List<String>> mRequest; + private final boolean mDoDryRun; + private final int mReason; + private final @NonNull String mCallingPackage; + private final @NonNull OnRevokeRuntimePermissionsCallback mCallback; + + private final @NonNull RemoteCallback mRemoteCallback; + + private PendingRevokeRuntimePermissionRequest(@NonNull RemoteService service, + @NonNull Map<String, List<String>> request, boolean doDryRun, + @Reason int reason, @NonNull String callingPackage, + @NonNull @CallbackExecutor Executor executor, + @NonNull OnRevokeRuntimePermissionsCallback callback) { + super(service); + + mRequest = request; + mDoDryRun = doDryRun; + mReason = reason; + mCallingPackage = callingPackage; + mCallback = callback; + + mRemoteCallback = new RemoteCallback(result -> executor.execute(() -> { + long token = Binder.clearCallingIdentity(); + try { + Map<String, List<String>> revoked = new ArrayMap<>(); + try { + Bundle bundleizedRevoked = result.getBundle(KEY_RESULT); + + for (String packageName : bundleizedRevoked.keySet()) { + Preconditions.checkNotNull(packageName); + + ArrayList<String> permissions = + bundleizedRevoked.getStringArrayList(packageName); + Preconditions.checkCollectionElementsNotNull(permissions, + "permissions"); + + revoked.put(packageName, permissions); + } + } catch (Exception e) { + Log.e(TAG, "Could not read result when revoking runtime permissions", e); + } + + callback.onRevokeRuntimePermissions(revoked); + } finally { + Binder.restoreCallingIdentity(token); + + finish(); + } + }), null); + } + + @Override + protected void onTimeout(RemoteService remoteService) { + mCallback.onRevokeRuntimePermissions(Collections.emptyMap()); + } + + @Override + public void run() { + Bundle bundledizedRequest = new Bundle(); + for (Map.Entry<String, List<String>> appRequest : mRequest.entrySet()) { + bundledizedRequest.putStringArrayList(appRequest.getKey(), + new ArrayList<>(appRequest.getValue())); + } + + try { + getService().getServiceInterface().revokeRuntimePermissions(bundledizedRequest, + mDoDryRun, mReason, mCallingPackage, mRemoteCallback); + } catch (RemoteException e) { + Log.e(TAG, "Error revoking runtime permission", e); + } + } + } + + /** * Request for {@link #getAppPermissions} */ private static final class PendingGetAppPermissionRequest extends diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java index 5dad07178e53..f621737e5ed4 100644 --- a/core/java/android/permission/PermissionControllerService.java +++ b/core/java/android/permission/PermissionControllerService.java @@ -16,6 +16,7 @@ package android.permission; +import static com.android.internal.util.Preconditions.checkArgument; import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull; import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; @@ -26,12 +27,19 @@ import android.annotation.SystemApi; import android.app.Service; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteCallback; +import android.util.ArrayMap; +import com.android.internal.util.Preconditions; + +import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * This service is meant to be implemented by the app controlling permissions. @@ -60,6 +68,20 @@ public abstract class PermissionControllerService extends Service { } /** + * Revoke a set of runtime permissions for various apps. + * + * @param requests The permissions to revoke as {@code Map<packageName, List<permission>>} + * @param doDryRun Compute the permissions that would be revoked, but not actually revoke them + * @param reason Why the permission should be revoked + * @param callerPackageName The package name of the calling app + * + * @return the actually removed permissions as {@code Map<packageName, List<permission>>} + */ + public abstract @NonNull Map<String, List<String>> onRevokeRuntimePermissions( + @NonNull Map<String, List<String>> requests, boolean doDryRun, + @PermissionControllerManager.Reason int reason, @NonNull String callerPackageName); + + /** * Gets the runtime permissions for an app. * * @param packageName The package for which to query. @@ -94,6 +116,41 @@ public abstract class PermissionControllerService extends Service { public final IBinder onBind(Intent intent) { return new IPermissionController.Stub() { @Override + public void revokeRuntimePermissions( + Bundle bundleizedRequest, boolean doDryRun, int reason, + String callerPackageName, RemoteCallback callback) { + checkNotNull(bundleizedRequest, "bundleizedRequest"); + checkNotNull(callerPackageName); + checkNotNull(callback); + + Map<String, List<String>> request = new ArrayMap<>(); + for (String packageName : bundleizedRequest.keySet()) { + Preconditions.checkNotNull(packageName); + + ArrayList<String> permissions = + bundleizedRequest.getStringArrayList(packageName); + Preconditions.checkCollectionElementsNotNull(permissions, "permissions"); + + request.put(packageName, permissions); + } + + enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, null); + + // Verify callerPackageName + try { + PackageInfo pkgInfo = getPackageManager().getPackageInfo(callerPackageName, 0); + checkArgument(getCallingUid() == pkgInfo.applicationInfo.uid); + } catch (PackageManager.NameNotFoundException e) { + throw new RuntimeException(e); + } + + mHandler.sendMessage(obtainMessage( + PermissionControllerService::revokeRuntimePermissions, + PermissionControllerService.this, request, doDryRun, reason, + callerPackageName, callback)); + } + + @Override public void getAppPermissions(String packageName, RemoteCallback callback) { checkNotNull(packageName, "packageName"); checkNotNull(callback, "callback"); @@ -133,6 +190,27 @@ public abstract class PermissionControllerService extends Service { }; } + private void revokeRuntimePermissions(@NonNull Map<String, List<String>> requests, + boolean doDryRun, @PermissionControllerManager.Reason int reason, + @NonNull String callerPackageName, @NonNull RemoteCallback callback) { + Map<String, List<String>> revoked = onRevokeRuntimePermissions(requests, + doDryRun, reason, callerPackageName); + + checkNotNull(revoked); + Bundle bundledizedRevoked = new Bundle(); + for (Map.Entry<String, List<String>> appRevocation : revoked.entrySet()) { + checkNotNull(appRevocation.getKey()); + checkCollectionElementsNotNull(appRevocation.getValue(), "permissions"); + + bundledizedRevoked.putStringArrayList(appRevocation.getKey(), + new ArrayList<>(appRevocation.getValue())); + } + + Bundle result = new Bundle(); + result.putBundle(PermissionControllerManager.KEY_RESULT, bundledizedRevoked); + callback.sendResult(result); + } + private void getAppPermissions(@NonNull String packageName, @NonNull RemoteCallback callback) { List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName); if (permissions != null && !permissions.isEmpty()) { diff --git a/core/java/android/permission/TEST_MAPPING b/core/java/android/permission/TEST_MAPPING new file mode 100644 index 000000000000..ba9f36a31f2e --- /dev/null +++ b/core/java/android/permission/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "CtsPermissionTestCases", + "options": [ + { + "include-filter": "android.permission.cts.PermissionControllerTest" + } + ] + } + ] +}
\ No newline at end of file diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index bb1784af6c85..5bb5ee880398 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6207,7 +6207,7 @@ public final class Settings { public static final String CHARGING_SOUNDS_ENABLED = "charging_sounds_enabled"; /** - * Whether to vibrate for wireless charging events. + * Whether to vibrate for charging events. * @hide */ public static final String CHARGING_VIBRATION_ENABLED = "charging_vibration_enabled"; @@ -8882,6 +8882,14 @@ public final class Settings { public static final String ADD_USERS_WHEN_LOCKED = "add_users_when_locked"; /** + * Whether applying ramping ringer on incoming phone call ringtone. + * <p>1 = apply ramping ringer + * <p>0 = do not apply ramping ringer + * @hide + */ + public static final String APPLY_RAMPING_RINGER = "apply_ramping_ringer"; + + /** * Setting whether the global gesture for enabling accessibility is enabled. * If this gesture is enabled the user will be able to perfrom it to enable * the accessibility state without visiting the settings app. @@ -9415,7 +9423,8 @@ public final class Settings { "hdmi_control_auto_wakeup_enabled"; /** - * Whether TV will also turn off other CEC devices when it goes to standby mode. + * Whether TV or Audio System will also turn off other CEC devices when it goes to standby + * mode. * (0 = false, 1 = true) * * @hide @@ -9424,6 +9433,15 @@ public final class Settings { "hdmi_control_auto_device_off_enabled"; /** + * Whether Audio System will also turn off TV when it goes to standby mode. + * (0 = false, 1 = true) + * + * @hide + */ + public static final String HDMI_CONTROL_AUTO_TV_OFF_ENABLED = + "hdmi_control_auto_tv_off_enabled"; + + /** * If <b>true</b>, enables out-of-the-box execution for priv apps. * Default: false * Values: 0 = false, 1 = true diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java index 7683b8ae08b2..a9f4034c37af 100644 --- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java +++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java @@ -214,7 +214,7 @@ public abstract class AugmentedAutofillService extends Service { } @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + protected final void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mAutofillProxies != null) { final int size = mAutofillProxies.size(); pw.print("Number proxies: "); pw.println(size); @@ -225,6 +225,15 @@ public abstract class AugmentedAutofillService extends Service { proxy.dump(" ", pw); } } + dump(pw, args); + } + + /** + * Implementation specific {@code dump}. + */ + protected void dump(@NonNull PrintWriter pw, + @SuppressWarnings("unused") @NonNull String[] args) { + pw.print(getClass().getName()); pw.println(": nothing to dump"); } /** @hide */ diff --git a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java index 784e71993815..db242a2e265e 100644 --- a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java +++ b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java @@ -34,7 +34,7 @@ import java.util.List; @SystemApi @Deprecated public final class ContentCaptureEventsRequest implements Parcelable { -// TODO(b/121033016): remove .java and .aidl once service implementation doesn't use it anymore +// TODO(b/121051220): remove .java and .aidl once service implementation doesn't use it anymore private final ContentCaptureEvent mEvent; diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index c9d46dd701b5..26bf361a8acc 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -123,12 +123,12 @@ public abstract class ContentCaptureService extends Service { }; /** - * List of sessions per UID. + * UIDs associated with each session. * * <p>This map is populated when an session is started, which is called by the system server * and can be trusted. Then subsequent calls made by the app are verified against this map. */ - private final ArrayMap<String, Integer> mSessionsByUid = new ArrayMap<>(); + private final ArrayMap<String, Integer> mSessionUids = new ArrayMap<>(); @CallSuper @Override @@ -285,13 +285,13 @@ public abstract class ContentCaptureService extends Service { @Override @CallSuper protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - final int size = mSessionsByUid.size(); + final int size = mSessionUids.size(); pw.print("Number sessions: "); pw.println(size); if (size > 0) { final String prefix = " "; for (int i = 0; i < size; i++) { - pw.print(prefix); pw.print(mSessionsByUid.keyAt(i)); - pw.print(": uid="); pw.println(mSessionsByUid.valueAt(i)); + pw.print(prefix); pw.print(mSessionUids.keyAt(i)); + pw.print(": uid="); pw.println(mSessionUids.valueAt(i)); } } } @@ -309,7 +309,7 @@ public abstract class ContentCaptureService extends Service { private void handleOnCreateSession(@NonNull ContentCaptureContext context, @NonNull String sessionId, int uid, IResultReceiver clientReceiver) { - mSessionsByUid.put(sessionId, uid); + mSessionUids.put(sessionId, uid); onCreateContentCaptureSession(context, new ContentCaptureSessionId(sessionId)); setClientState(clientReceiver, ContentCaptureSession.STATE_ACTIVE, mClientInterface.asBinder()); @@ -336,11 +336,11 @@ public abstract class ContentCaptureService extends Service { case ContentCaptureEvent.TYPE_SESSION_STARTED: final ContentCaptureContext clientContext = event.getClientContext(); clientContext.setParentSessionId(event.getParentSessionId()); - mSessionsByUid.put(sessionIdString, uid); + mSessionUids.put(sessionIdString, uid); onCreateContentCaptureSession(clientContext, sessionId); break; case ContentCaptureEvent.TYPE_SESSION_FINISHED: - mSessionsByUid.remove(sessionIdString); + mSessionUids.remove(sessionIdString); onDestroyContentCaptureSession(sessionId); break; default: @@ -355,7 +355,7 @@ public abstract class ContentCaptureService extends Service { } private void handleFinishSession(@NonNull String sessionId) { - mSessionsByUid.remove(sessionId); + mSessionUids.remove(sessionId); onDestroyContentCaptureSession(new ContentCaptureSessionId(sessionId)); } @@ -372,10 +372,13 @@ public abstract class ContentCaptureService extends Service { default: sessionId = event.getSessionId(); } - final Integer rightUid = mSessionsByUid.get(sessionId); + final Integer rightUid = mSessionUids.get(sessionId); if (rightUid == null) { - if (VERBOSE) Log.v(TAG, "No session for " + sessionId); - // Just ignore, as the session could have finished + if (DEBUG) { + Log.d(TAG, "handleIsRightCallerFor(" + event + "): no session for " + sessionId + + ": " + mSessionUids); + } + // Just ignore, as the session could have been finished already return false; } if (rightUid != uid) { diff --git a/core/java/android/service/wallpaper/IWallpaperConnection.aidl b/core/java/android/service/wallpaper/IWallpaperConnection.aidl index a976d0e40423..f334d9d3e874 100644 --- a/core/java/android/service/wallpaper/IWallpaperConnection.aidl +++ b/core/java/android/service/wallpaper/IWallpaperConnection.aidl @@ -27,5 +27,5 @@ interface IWallpaperConnection { void attachEngine(IWallpaperEngine engine, int displayId); void engineShown(IWallpaperEngine engine); ParcelFileDescriptor setWallpaper(String name); - void onWallpaperColorsChanged(in WallpaperColors colors); + void onWallpaperColorsChanged(in WallpaperColors colors, int displayId); } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index a011d677fc6a..984846e33b1d 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -56,6 +56,7 @@ import android.view.InputEvent; import android.view.InputEventReceiver; import android.view.InsetsState; import android.view.MotionEvent; +import android.view.SurfaceControl; import android.view.SurfaceHolder; import android.view.View; import android.view.ViewGroup; @@ -217,6 +218,8 @@ public abstract class WallpaperService extends Service { private Context mDisplayContext; private int mDisplayState; + SurfaceControl mSurfaceControl = new SurfaceControl(); + final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() { { mRequestedFormat = PixelFormat.RGBX_8888; @@ -642,7 +645,7 @@ public abstract class WallpaperService extends Service { try { final WallpaperColors newColors = onComputeColors(); if (mConnection != null) { - mConnection.onWallpaperColorsChanged(newColors); + mConnection.onWallpaperColorsChanged(newColors, mDisplay.getDisplayId()); } else { Log.w(TAG, "Can't notify system because wallpaper connection " + "was not established."); @@ -843,8 +846,12 @@ public abstract class WallpaperService extends Service { mWindow, mWindow.mSeq, mLayout, mWidth, mHeight, View.VISIBLE, 0, -1, mWinFrame, mOverscanInsets, mContentInsets, mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame, - mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface, + mDisplayCutout, mMergedConfiguration, mSurfaceControl, mInsetsState); + if (mSurfaceControl.isValid()) { + mSurfaceHolder.mSurface.copyFrom(mSurfaceControl); + mSurfaceControl.release(); + } if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface + ", frame=" + mWinFrame); @@ -1466,7 +1473,7 @@ public abstract class WallpaperService extends Service { break; } try { - mConnection.onWallpaperColorsChanged(mEngine.onComputeColors()); + mConnection.onWallpaperColorsChanged(mEngine.onComputeColors(), mDisplayId); } catch (RemoteException e) { // Connection went away, nothing to do in here. } diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index d1115c7b4d1d..658f06ad21f9 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -99,7 +99,7 @@ interface IWindowSession { out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets, out Rect outOutsets, out Rect outBackdropFrame, out DisplayCutout.ParcelableWrapper displayCutout, - out MergedConfiguration outMergedConfiguration, out Surface outSurface, + out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl, out InsetsState insetsState); /* diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 1a708e8e30ec..0b6beba06555 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -67,6 +67,7 @@ public class SurfaceControl implements Parcelable { int w, int h, int format, int flags, long parentObject, int windowType, int ownerUid) throws OutOfResourcesException; private static native long nativeReadFromParcel(Parcel in); + private static native long nativeCopyFromSurfaceControl(long nativeObject); private static native void nativeWriteToParcel(long nativeObject, Parcel out); private static native void nativeRelease(long nativeObject); private static native void nativeDestroy(long nativeObject); @@ -169,7 +170,7 @@ public class SurfaceControl implements Parcelable { IBinder toToken); private final CloseGuard mCloseGuard = CloseGuard.get(); - private final String mName; + private String mName; long mNativeObject; // package visibility only for Surface.java access // TODO: Move this to native. @@ -359,6 +360,13 @@ public class SurfaceControl implements Parcelable { */ public static final int WINDOW_TYPE_DONT_SCREENSHOT = 441731; + public void copyFrom(SurfaceControl other) { + mName = other.mName; + mWidth = other.mWidth; + mHeight = other.mHeight; + mNativeObject = nativeCopyFromSurfaceControl(other.mNativeObject); + } + /** * Builder class for {@link SurfaceControl} objects. */ @@ -660,14 +668,29 @@ public class SurfaceControl implements Parcelable { } private SurfaceControl(Parcel in) { + readFromParcel(in); + mCloseGuard.open("release"); + } + + public SurfaceControl() { + mCloseGuard.open("release"); + } + + public void readFromParcel(Parcel in) { + if (in == null) { + throw new IllegalArgumentException("source must not be null"); + } + mName = in.readString(); mWidth = in.readInt(); mHeight = in.readInt(); - mNativeObject = nativeReadFromParcel(in); - if (mNativeObject == 0) { - throw new IllegalArgumentException("Couldn't read SurfaceControl from parcel=" + in); + + release(); + if (in.readInt() != 0) { + mNativeObject = nativeReadFromParcel(in); + } else { + mNativeObject = 0; } - mCloseGuard.open("release"); } @Override @@ -680,7 +703,16 @@ public class SurfaceControl implements Parcelable { dest.writeString(mName); dest.writeInt(mWidth); dest.writeInt(mHeight); + if (mNativeObject == 0) { + dest.writeInt(0); + } else { + dest.writeInt(1); + } nativeWriteToParcel(mNativeObject, dest); + + if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) { + release(); + } } /** @@ -763,6 +795,10 @@ public class SurfaceControl implements Parcelable { "mNativeObject is null. Have you called release() already?"); } + public boolean isValid() { + return mNativeObject != 0; + } + /* * set surface parameters. * needs to be inside open/closeTransaction block diff --git a/core/java/android/view/SurfaceSession.java b/core/java/android/view/SurfaceSession.java index a4fa12a57e93..361ac932758e 100644 --- a/core/java/android/view/SurfaceSession.java +++ b/core/java/android/view/SurfaceSession.java @@ -30,7 +30,6 @@ public final class SurfaceSession { private long mNativeClient; // SurfaceComposerClient* private static native long nativeCreate(); - private static native long nativeCreateScoped(long surfacePtr); private static native void nativeDestroy(long ptr); private static native void nativeKill(long ptr); @@ -40,15 +39,6 @@ public final class SurfaceSession { mNativeClient = nativeCreate(); } - public SurfaceSession(Surface root) { - synchronized (root.mLock) { - if (root.mNativeObject == 0) { - throw new IllegalStateException("Surface is not initialized or has been released"); - } - mNativeClient = nativeCreateScoped(root.mNativeObject); - } - } - /* no user serviceable parts here ... */ @Override protected void finalize() throws Throwable { diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index a0af83d17abd..61fb00d3fe59 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -547,7 +547,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb if (creating) { viewRoot.createBoundsSurface(mSubLayer); - mSurfaceSession = new SurfaceSession(viewRoot.mBoundsSurface); + mSurfaceSession = new SurfaceSession(); mDeferredDestroySurfaceControl = mSurfaceControl; updateOpaqueFlag(); @@ -559,6 +559,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb new SurfaceControl.Builder(mSurfaceSession) .setBufferSize(mSurfaceWidth, mSurfaceHeight) .setFormat(mFormat) + .setParent(viewRoot.getSurfaceControl()) .setFlags(mSurfaceFlags)); } else if (mSurfaceControl == null) { return; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 6b07efc0b448..9dfbf28f925a 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -7660,10 +7660,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Convenience method for sending a {@link AccessibilityEvent#TYPE_ANNOUNCEMENT} - * {@link AccessibilityEvent} to make an announcement which is related to some - * sort of a context change for which none of the events representing UI transitions - * is a good fit. For example, announcing a new page in a book. If accessibility - * is not enabled this method does nothing. + * {@link AccessibilityEvent} to suggest that an accessibility service announce the + * specified text to its users. + * <p> + * Note: The event generated with this API carries no semantic meaning, and is appropriate only + * in exceptional situations. Apps can generally achieve correct behavior for accessibility by + * accurately supplying the semantics of their UI. + * They should not need to specify what exactly is announced to users. * * @param text The announcement text. */ @@ -25053,9 +25056,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } final ViewRootImpl root = mAttachInfo.mViewRootImpl; - final SurfaceSession session = new SurfaceSession(root.mSurface); + final SurfaceSession session = new SurfaceSession(); final SurfaceControl surfaceControl = new SurfaceControl.Builder(session) .setName("drag surface") + .setParent(root.getSurfaceControl()) .setBufferSize(shadowSize.x, shadowSize.y) .setFormat(PixelFormat.TRANSLUCENT) .build(); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 3f7a5127339d..c0b428359efc 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -432,6 +432,7 @@ public final class ViewRootImpl implements ViewParent, // Surface can never be reassigned or cleared (use Surface.clear()). @UnsupportedAppUsage public final Surface mSurface = new Surface(); + private final SurfaceControl mSurfaceControl = new SurfaceControl(); /** * Child surface of {@code mSurface} with the same bounds as its parent, and crop bounds @@ -1526,7 +1527,7 @@ public final class ViewRootImpl implements ViewParent, */ public void createBoundsSurface(int zOrderLayer) { if (mSurfaceSession == null) { - mSurfaceSession = new SurfaceSession(mSurface); + mSurfaceSession = new SurfaceSession(); } if (mBoundsSurfaceControl != null && mBoundsSurface.isValid()) { return; // surface control for bounds surface already exists. @@ -1534,6 +1535,7 @@ public final class ViewRootImpl implements ViewParent, mBoundsSurfaceControl = new SurfaceControl.Builder(mSurfaceSession) .setName("Bounds for - " + getTitle().toString()) + .setParent(mSurfaceControl) .build(); setBoundsSurfaceCrop(); @@ -1567,6 +1569,8 @@ public final class ViewRootImpl implements ViewParent, private void destroySurface() { mSurface.release(); + mSurfaceControl.release(); + mSurfaceSession = null; if (mBoundsSurfaceControl != null) { @@ -6801,7 +6805,12 @@ public final class ViewRootImpl implements ViewParent, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber, mTmpFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets, mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout, - mPendingMergedConfiguration, mSurface, mTempInsets); + mPendingMergedConfiguration, mSurfaceControl, mTempInsets); + if (mSurfaceControl.isValid()) { + mSurface.copyFrom(mSurfaceControl); + } else { + destroySurface(); + } mPendingAlwaysConsumeNavBar = (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0; @@ -8483,6 +8492,10 @@ public final class ViewRootImpl implements ViewParent, mActivityRelaunched = true; } + public SurfaceControl getSurfaceControl() { + return mSurfaceControl; + } + /** * Class for managing the accessibility interaction connection * based on the global accessibility state. diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 56f973eb9e46..90ccc257b134 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -62,6 +62,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.IResultReceiver; import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; +import com.android.internal.util.SyncResultReceiver; import org.xmlpull.v1.XmlPullParserException; @@ -75,8 +76,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; + //TODO: use java.lang.ref.Cleaner once Android supports Java 9 import sun.misc.Cleaner; @@ -324,6 +324,11 @@ public final class AutofillManager { public static final int FC_SERVICE_TIMEOUT = 5000; /** + * Timeout for calls to system_server. + */ + private static final int SYNC_CALLS_TIMEOUT_MS = 5000; + + /** * Makes an authentication id from a request id and a dataset id. * * @param requestId The request id. @@ -612,7 +617,8 @@ public final class AutofillManager { final AutofillClient client = getClient(); if (client != null) { - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver( + SYNC_CALLS_TIMEOUT_MS); try { mService.restoreSession(mSessionId, client.autofillClientGetActivityToken(), mServiceClient.asBinder(), receiver); @@ -732,9 +738,9 @@ public final class AutofillManager { */ @Nullable public FillEventHistory getFillEventHistory() { try { - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); mService.getFillEventHistory(receiver); - return receiver.getObjectResult(SyncResultReceiver.TYPE_PARCELABLE); + return receiver.getParcelableResult(); } catch (RemoteException e) { e.rethrowFromSystemServer(); return null; @@ -1287,7 +1293,7 @@ public final class AutofillManager { public boolean hasEnabledAutofillServices() { if (mService == null) return false; - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); try { mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName(), receiver); return receiver.getIntResult() == 1; @@ -1304,10 +1310,10 @@ public final class AutofillManager { public ComponentName getAutofillServiceComponentName() { if (mService == null) return null; - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); try { mService.getAutofillServiceComponentName(receiver); - return receiver.getObjectResult(SyncResultReceiver.TYPE_PARCELABLE); + return receiver.getParcelableResult(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1330,9 +1336,9 @@ public final class AutofillManager { */ @Nullable public String getUserDataId() { try { - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); mService.getUserDataId(receiver); - return receiver.getObjectResult(SyncResultReceiver.TYPE_STRING); + return receiver.getStringResult(); } catch (RemoteException e) { e.rethrowFromSystemServer(); return null; @@ -1352,9 +1358,9 @@ public final class AutofillManager { */ @Nullable public UserData getUserData() { try { - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); mService.getUserData(receiver); - return receiver.getObjectResult(SyncResultReceiver.TYPE_PARCELABLE); + return receiver.getParcelableResult(); } catch (RemoteException e) { e.rethrowFromSystemServer(); return null; @@ -1390,7 +1396,7 @@ public final class AutofillManager { * the user. */ public boolean isFieldClassificationEnabled() { - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); try { mService.isFieldClassificationEnabled(receiver); return receiver.getIntResult() == 1; @@ -1413,10 +1419,10 @@ public final class AutofillManager { */ @Nullable public String getDefaultFieldClassificationAlgorithm() { - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); try { mService.getDefaultFieldClassificationAlgorithm(receiver); - return receiver.getObjectResult(SyncResultReceiver.TYPE_STRING); + return receiver.getStringResult(); } catch (RemoteException e) { e.rethrowFromSystemServer(); return null; @@ -1433,11 +1439,10 @@ public final class AutofillManager { */ @NonNull public List<String> getAvailableFieldClassificationAlgorithms() { - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); try { mService.getAvailableFieldClassificationAlgorithms(receiver); - final String[] algorithms = receiver - .getObjectResult(SyncResultReceiver.TYPE_STRING_ARRAY); + final String[] algorithms = receiver.getStringArrayResult(); return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList(); } catch (RemoteException e) { e.rethrowFromSystemServer(); @@ -1458,7 +1463,7 @@ public final class AutofillManager { public boolean isAutofillSupported() { if (mService == null) return false; - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); try { mService.isServiceSupported(mContext.getUserId(), receiver); return receiver.getIntResult() == 1; @@ -1582,7 +1587,7 @@ public final class AutofillManager { final AutofillClient client = getClient(); if (client == null) return; // NOTE: getClient() already logged it.. - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); mService.startSession(client.autofillClientGetActivityToken(), mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(), mCallback != null, flags, client.autofillClientGetComponentName(), @@ -1665,7 +1670,7 @@ public final class AutofillManager { mServiceClient = new AutofillManagerClient(this); try { final int userId = mContext.getUserId(); - final SyncResultReceiver receiver = new SyncResultReceiver(); + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); mService.addClient(mServiceClient, userId, receiver); final int flags = receiver.getIntResult(); mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0; @@ -2986,104 +2991,4 @@ public final class AutofillManager { } } } - - /** - * @hide - */ - public static final class SyncResultReceiver extends IResultReceiver.Stub { - - private static final String EXTRA = "EXTRA"; - - /** - * How long to block waiting for {@link IResultReceiver} callbacks when calling server. - */ - private static final long BINDER_TIMEOUT_MS = 5000; - - private static final int TYPE_STRING = 0; - private static final int TYPE_STRING_ARRAY = 1; - private static final int TYPE_PARCELABLE = 2; - - private final CountDownLatch mLatch = new CountDownLatch(1); - private int mResult; - private Bundle mBundle; - - private void waitResult() { - try { - if (!mLatch.await(BINDER_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { - throw new IllegalStateException("Not called in " + BINDER_TIMEOUT_MS + "ms"); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - - /** - * Gets the result from an operation that returns an {@code int}. - */ - int getIntResult() { - waitResult(); - return mResult; - } - - /** - * Gets the result from an operation that returns an {@code Object}. - * - * @param type type of expected object. - */ - @Nullable - @SuppressWarnings("unchecked") - <T> T getObjectResult(int type) { - waitResult(); - if (mBundle == null) { - return null; - } - switch (type) { - case TYPE_STRING: - return (T) mBundle.getString(EXTRA); - case TYPE_STRING_ARRAY: - return (T) mBundle.getStringArray(EXTRA); - case TYPE_PARCELABLE: - return (T) mBundle.getParcelable(EXTRA); - default: - throw new IllegalArgumentException("unsupported type: " + type); - } - } - - @Override - public void send(int resultCode, Bundle resultData) { - mResult = resultCode; - mBundle = resultData; - mLatch.countDown(); - } - - /** - * Creates a bundle for a {@code String} value. - */ - @NonNull - public static Bundle bundleFor(@Nullable String value) { - final Bundle bundle = new Bundle(); - bundle.putString(EXTRA, value); - return bundle; - } - - /** - * Creates a bundle for a {@code String[]} value. - */ - @NonNull - public static Bundle bundleFor(@Nullable String[] value) { - final Bundle bundle = new Bundle(); - bundle.putStringArray(EXTRA, value); - return bundle; - } - - /** - * Creates a bundle for a {@code Parcelable} value. - */ - @NonNull - public static Bundle bundleFor(@Nullable Parcelable value) { - final Bundle bundle = new Bundle(); - bundle.putParcelable(EXTRA, value); - return bundle; - } - } } diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java index 51668319cde2..04e725e64e9c 100644 --- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java @@ -33,7 +33,7 @@ import java.io.PrintWriter; final class ChildContentCaptureSession extends ContentCaptureSession { @NonNull - private final MainContentCaptureSession mParent; + private final ContentCaptureSession mParent; /** * {@link ContentCaptureContext} set by client, or {@code null} when it's the @@ -46,16 +46,25 @@ final class ChildContentCaptureSession extends ContentCaptureSession { private final ContentCaptureContext mClientContext; /** @hide */ - protected ChildContentCaptureSession(@NonNull MainContentCaptureSession parent, + protected ChildContentCaptureSession(@NonNull ContentCaptureSession parent, @NonNull ContentCaptureContext clientContext) { mParent = parent; mClientContext = Preconditions.checkNotNull(clientContext); } @Override - ContentCaptureSession newChild(@NonNull ContentCaptureContext context) { - // TODO(b/121033016): implement it - throw new UnsupportedOperationException("grand-children not implemented yet"); + MainContentCaptureSession getMainCaptureSession() { + if (mParent instanceof MainContentCaptureSession) { + return (MainContentCaptureSession) mParent; + } + return mParent.getMainCaptureSession(); + } + + @Override + ContentCaptureSession newChild(@NonNull ContentCaptureContext clientContext) { + final ContentCaptureSession child = new ChildContentCaptureSession(this, clientContext); + getMainCaptureSession().notifyChildSessionStarted(mId, child.mId, clientContext); + return child; } @Override @@ -65,27 +74,27 @@ final class ChildContentCaptureSession extends ContentCaptureSession { @Override void onDestroy() { - mParent.notifyChildSessionFinished(mParent.mId, mId); + getMainCaptureSession().notifyChildSessionFinished(mParent.mId, mId); } @Override void internalNotifyViewAppeared(@NonNull ViewStructureImpl node) { - mParent.notifyViewAppeared(mId, node); + getMainCaptureSession().notifyViewAppeared(mId, node); } @Override void internalNotifyViewDisappeared(@NonNull AutofillId id) { - mParent.notifyViewDisappeared(mId, id); + getMainCaptureSession().notifyViewDisappeared(mId, id); } @Override void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text, int flags) { - mParent.notifyViewTextChanged(mId, id, text, flags); + getMainCaptureSession().notifyViewTextChanged(mId, id, text, flags); } @Override boolean isContentCaptureEnabled() { - return mParent.isContentCaptureEnabled(); + return getMainCaptureSession().isContentCaptureEnabled(); } @Override diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java index 9e3da9234b58..0d064307ce54 100644 --- a/core/java/android/view/contentcapture/ContentCaptureEvent.java +++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java @@ -251,6 +251,10 @@ public final class ContentCaptureEvent implements Parcelable { public String toString() { final StringBuilder string = new StringBuilder("ContentCaptureEvent[type=") .append(getTypeAsString(mType)); + string.append(", session=").append(mSessionId); + if (mType == TYPE_SESSION_STARTED && mParentSessionId != null) { + string.append(", parent=").append(mParentSessionId); + } if (mFlags > 0) { string.append(", flags=").append(mFlags); } diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index 983079073d02..e962e7c8fe41 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -117,13 +117,11 @@ public final class ContentCaptureManager { /** @hide */ public void onActivityStarted(@NonNull IBinder applicationToken, @NonNull ComponentName activityComponent) { - // TODO(b/121033016): must start all sessions getMainContentCaptureSession().start(applicationToken, activityComponent); } /** @hide */ public void onActivityStopped() { - // TODO(b/121033016): must finish all sessions getMainContentCaptureSession().destroy(); } @@ -135,7 +133,6 @@ public final class ContentCaptureManager { * @hide */ public void flush() { - // TODO(b/121033016): must flush all sessions getMainContentCaptureSession().flush(); } diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index 46e6882b8643..344b9973fdb1 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -27,6 +27,7 @@ import android.view.ViewStructure; import android.view.autofill.AutofillId; import android.view.contentcapture.ViewNode.ViewStructureImpl; +import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import dalvik.system.CloseGuard; @@ -34,7 +35,6 @@ import dalvik.system.CloseGuard; import java.io.PrintWriter; import java.util.ArrayList; import java.util.UUID; -import java.util.concurrent.atomic.AtomicBoolean; /** * Session used to notify a system-provided Content Capture service about events associated with @@ -73,11 +73,11 @@ public abstract class ContentCaptureSession implements AutoCloseable { public static final int STATE_ACTIVE = 2; /** - * Session is disabled. + * Session is disabled because there is no service for this user. * * @hide */ - public static final int STATE_DISABLED = 3; + public static final int STATE_DISABLED_NO_SERVICE = 3; /** * Session is disabled because its id already existed on server. @@ -90,11 +90,14 @@ public abstract class ContentCaptureSession implements AutoCloseable { private final CloseGuard mCloseGuard = CloseGuard.get(); + private final Object mLock = new Object(); + /** * Guard use to ignore events after it's destroyed. */ @NonNull - private final AtomicBoolean mDestroyed = new AtomicBoolean(); + @GuardedBy("mLock") + private boolean mDestroyed; /** @hide */ @Nullable @@ -108,11 +111,8 @@ public abstract class ContentCaptureSession implements AutoCloseable { /** * List of children session. */ - // TODO(b/121033016): need to synchonize access, either by changing on handler or UI thread - // (for now there's no handler on this class, so we need to wait for the next refactoring), - // most likely the former (as we have no guarantee that createContentCaptureSession() - // it will be called in the UiThread; for example, WebView most likely won't call on it) @Nullable + @GuardedBy("mLock") private ArrayList<ContentCaptureSession> mChildren; /** @hide */ @@ -120,6 +120,10 @@ public abstract class ContentCaptureSession implements AutoCloseable { mCloseGuard.open("destroy"); } + /** @hide */ + @NonNull + abstract MainContentCaptureSession getMainCaptureSession(); + /** * Gets the id used to identify this session. */ @@ -143,10 +147,12 @@ public abstract class ContentCaptureSession implements AutoCloseable { Log.d(TAG, "createContentCaptureSession(" + context + ": parent=" + mId + ", child=" + child.mId); } - if (mChildren == null) { - mChildren = new ArrayList<>(INITIAL_CHILDREN_CAPACITY); + synchronized (mLock) { + if (mChildren == null) { + mChildren = new ArrayList<>(INITIAL_CHILDREN_CAPACITY); + } + mChildren.add(child); } - mChildren.add(child); return child; } @@ -163,29 +169,31 @@ public abstract class ContentCaptureSession implements AutoCloseable { * <p>Once destroyed, any new notification will be dropped. */ public final void destroy() { - if (!mDestroyed.compareAndSet(false, true)) { - Log.e(TAG, "destroy(): already destroyed"); - return; - } - - mCloseGuard.close(); + synchronized (mLock) { + if (mDestroyed) { + Log.e(TAG, "destroy(" + mId + "): already destroyed"); + return; + } + mDestroyed = true; - //TODO(b/111276913): check state (for example, how to handle if it's waiting for remote - // id) and send it to the cache of batched commands - if (VERBOSE) { - Log.v(TAG, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId); - } + mCloseGuard.close(); - // Finish children first - if (mChildren != null) { - final int numberChildren = mChildren.size(); - if (VERBOSE) Log.v(TAG, "Destroying " + numberChildren + " children first"); - for (int i = 0; i < numberChildren; i++) { - final ContentCaptureSession child = mChildren.get(i); - try { - child.destroy(); - } catch (Exception e) { - Log.w(TAG, "exception destroying child session #" + i + ": " + e); + //TODO(b/111276913): check state (for example, how to handle if it's waiting for remote + // id) and send it to the cache of batched commands + if (VERBOSE) { + Log.v(TAG, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId); + } + // Finish children first + if (mChildren != null) { + final int numberChildren = mChildren.size(); + if (VERBOSE) Log.v(TAG, "Destroying " + numberChildren + " children first"); + for (int i = 0; i < numberChildren; i++) { + final ContentCaptureSession child = mChildren.get(i); + try { + child.destroy(); + } catch (Exception e) { + Log.w(TAG, "exception destroying child session #" + i + ": " + e); + } } } } @@ -305,23 +313,26 @@ public abstract class ContentCaptureSession implements AutoCloseable { } boolean isContentCaptureEnabled() { - return !mDestroyed.get(); + synchronized (mLock) { + return !mDestroyed; + } } @CallSuper void dump(@NonNull String prefix, @NonNull PrintWriter pw) { pw.print(prefix); pw.print("id: "); pw.println(mId); - pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed.get()); - if (mChildren != null && !mChildren.isEmpty()) { - final String prefix2 = prefix + " "; - final int numberChildren = mChildren.size(); - pw.print(prefix); pw.print("number children: "); pw.println(numberChildren); - for (int i = 0; i < numberChildren; i++) { - final ContentCaptureSession child = mChildren.get(i); - pw.print(prefix); pw.print(i); pw.println(": "); child.dump(prefix2, pw); + synchronized (mLock) { + pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed); + if (mChildren != null && !mChildren.isEmpty()) { + final String prefix2 = prefix + " "; + final int numberChildren = mChildren.size(); + pw.print(prefix); pw.print("number children: "); pw.println(numberChildren); + for (int i = 0; i < numberChildren; i++) { + final ContentCaptureSession child = mChildren.get(i); + pw.print(prefix); pw.print(i); pw.println(": "); child.dump(prefix2, pw); + } } } - } @Override @@ -341,8 +352,8 @@ public abstract class ContentCaptureSession implements AutoCloseable { return "WAITING_FOR_SERVER"; case STATE_ACTIVE: return "ACTIVE"; - case STATE_DISABLED: - return "DISABLED"; + case STATE_DISABLED_NO_SERVICE: + return "DISABLED_NO_SERVICE"; case STATE_DISABLED_DUPLICATED_ID: return "DISABLED_DUPLICATED_ID"; default: diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index baf4a35f179e..44a381e5533d 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -141,6 +141,11 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } @Override + MainContentCaptureSession getMainCaptureSession() { + return this; + } + + @Override ContentCaptureSession newChild(@NonNull ContentCaptureContext clientContext) { final ContentCaptureSession child = new ChildContentCaptureSession(this, clientContext); notifyChildSessionStarted(mId, child.mId, clientContext); @@ -171,6 +176,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { @Override void onDestroy() { + mHandler.removeMessages(MSG_FLUSH); mHandler.sendMessage( obtainMessage(MainContentCaptureSession::handleDestroySession, this)); } @@ -237,7 +243,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { Log.w(TAG, "Failed to link to death on " + binder + ": " + e); } } - if (resultCode == STATE_DISABLED || resultCode == STATE_DISABLED_DUPLICATED_ID) { + if (resultCode == STATE_DISABLED_NO_SERVICE || resultCode == STATE_DISABLED_DUPLICATED_ID) { mDisabled.set(true); handleResetSession(/* resetState= */ false); } else { @@ -246,7 +252,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { if (VERBOSE) { Log.v(TAG, "handleSessionStarted() result: code=" + resultCode + ", id=" + mId + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get() - + ", binder=" + binder); + + ", binder=" + binder + ", events=" + (mEvents == null ? 0 : mEvents.size())); } } @@ -285,14 +291,14 @@ public final class MainContentCaptureSession extends ContentCaptureSession { return; } - if (mState != STATE_ACTIVE) { + if (mState != STATE_ACTIVE && numberEvents >= MAX_BUFFER_SIZE) { // Callback from startSession hasn't been called yet - typically happens on system // apps that are started before the system service // TODO(b/111276913): try to ignore session while system is not ready / boot // not complete instead. Similarly, the manager service should return right away // when the user does not have a service set - if (VERBOSE) { - Log.v(TAG, "Closing session for " + getActivityDebugName() + if (DEBUG) { + Log.d(TAG, "Closing session for " + getActivityDebugName() + " after " + numberEvents + " delayed events and state " + getStateAsString(mState)); } @@ -331,7 +337,9 @@ public final class MainContentCaptureSession extends ContentCaptureSession { if (mEvents == null) return; if (mDirectServiceInterface == null) { - if (DEBUG) Log.d(TAG, "handleForceFlush(): hold your horses, client not ready yet!"); + if (VERBOSE) { + Log.v(TAG, "handleForceFlush(): hold your horses, client not ready: " + mEvents); + } if (!mHandler.hasMessages(MSG_FLUSH)) { handleScheduleFlush(/* checkExisting= */ false); } @@ -386,14 +394,14 @@ public final class MainContentCaptureSession extends ContentCaptureSession { handleResetSession(/* resetState= */ true); } - // TODO(b/121033016): once we support multiple sessions, we might need to move some of these + // TODO(b/122454205): once we support multiple sessions, we might need to move some of these // clearings out. private void handleResetSession(boolean resetState) { if (resetState) { mState = STATE_UNKNOWN; } - // TODO(b/121033016): must reset children (which currently is owned by superclass) + // TODO(b/122454205): must reset children (which currently is owned by superclass) mApplicationToken = null; mComponentName = null; mEvents = null; @@ -426,7 +434,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { && !mDisabled.get(); } - // TODO(b/121033016): refactor "notifyXXXX" methods below to a common "Buffer" object that is + // TODO(b/122454205): refactor "notifyXXXX" methods below to a common "Buffer" object that is // shared between ActivityContentCaptureSession and ChildContentCaptureSession objects. Such // change should also get get rid of the "internalNotifyXXXX" methods above void notifyChildSessionStarted(@NonNull String parentSessionId, @@ -435,14 +443,14 @@ public final class MainContentCaptureSession extends ContentCaptureSession { new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED) .setParentSessionId(parentSessionId) .setClientContext(clientContext), - /* forceFlush= */ false)); + /* forceFlush= */ true)); } void notifyChildSessionFinished(@NonNull String parentSessionId, @NonNull String childSessionId) { mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this, new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED) - .setParentSessionId(parentSessionId), /* forceFlush= */ false)); + .setParentSessionId(parentSessionId), /* forceFlush= */ true)); } void notifyViewAppeared(@NonNull String sessionId, @NonNull ViewStructureImpl node) { diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java index 7d027574198d..886718d192e0 100644 --- a/core/java/android/widget/Magnifier.java +++ b/core/java/android/widget/Magnifier.java @@ -271,7 +271,7 @@ public final class Magnifier { if (mWindow == null) { synchronized (mLock) { mWindow = new InternalPopupWindow(mView.getContext(), mView.getDisplay(), - mParentSurface.mSurface, mWindowWidth, mWindowHeight, + mParentSurface.mSurfaceControl, mWindowWidth, mWindowHeight, mWindowElevation, mWindowCornerRadius, mOverlay != null ? mOverlay : new ColorDrawable(Color.TRANSPARENT), Handler.getMain() /* draw the magnifier on the UI thread */, mLock, @@ -528,17 +528,20 @@ public final class Magnifier { final int surfaceHeight = viewRootImpl.getHeight() + surfaceInsets.top + surfaceInsets.bottom; validMainWindowSurface = - new SurfaceInfo(mainWindowSurface, surfaceWidth, surfaceHeight, true); + new SurfaceInfo(viewRootImpl.getSurfaceControl(), mainWindowSurface, + surfaceWidth, surfaceHeight, true); } } // Get the surface backing the magnified view, if it is a SurfaceView. SurfaceInfo validSurfaceViewSurface = SurfaceInfo.NULL; if (mView instanceof SurfaceView) { + final SurfaceControl sc = ((SurfaceView) mView).getSurfaceControl(); final SurfaceHolder surfaceHolder = ((SurfaceView) mView).getHolder(); final Surface surfaceViewSurface = surfaceHolder.getSurface(); - if (surfaceViewSurface != null && surfaceViewSurface.isValid()) { + + if (sc != null && sc.isValid()) { final Rect surfaceFrame = surfaceHolder.getSurfaceFrame(); - validSurfaceViewSurface = new SurfaceInfo(surfaceViewSurface, + validSurfaceViewSurface = new SurfaceInfo(sc, surfaceViewSurface, surfaceFrame.right, surfaceFrame.bottom, false); } } @@ -733,15 +736,18 @@ public final class Magnifier { * Contains a surface and metadata corresponding to it. */ private static class SurfaceInfo { - public static final SurfaceInfo NULL = new SurfaceInfo(null, 0, 0, false); + public static final SurfaceInfo NULL = new SurfaceInfo(null, null, 0, 0, false); private Surface mSurface; + private SurfaceControl mSurfaceControl; private int mWidth; private int mHeight; private boolean mIsMainWindowSurface; - SurfaceInfo(final Surface surface, final int width, final int height, + SurfaceInfo(final SurfaceControl surfaceControl, final Surface surface, + final int width, final int height, final boolean isMainWindowSurface) { + mSurfaceControl = surfaceControl; mSurface = surface; mWidth = width; mHeight = height; @@ -819,7 +825,7 @@ public final class Magnifier { private Bitmap mCurrentContent; InternalPopupWindow(final Context context, final Display display, - final Surface parentSurface, final int width, final int height, + final SurfaceControl parentSurfaceControl, final int width, final int height, final float elevation, final float cornerRadius, final Drawable overlay, final Handler handler, final Object lock, final Callback callback) { mDisplay = display; @@ -834,12 +840,13 @@ public final class Magnifier { // Setup the surface we will use for drawing the content and shadow. mSurfaceWidth = mContentWidth + 2 * mOffsetX; mSurfaceHeight = mContentHeight + 2 * mOffsetY; - mSurfaceSession = new SurfaceSession(parentSurface); + mSurfaceSession = new SurfaceSession(); mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession) .setFormat(PixelFormat.TRANSLUCENT) .setBufferSize(mSurfaceWidth, mSurfaceHeight) .setName("magnifier surface") .setFlags(SurfaceControl.HIDDEN) + .setParent(parentSurfaceControl) .build(); mSurface = new Surface(); mSurface.copyFrom(mSurfaceControl); diff --git a/core/java/com/android/internal/util/SyncResultReceiver.java b/core/java/com/android/internal/util/SyncResultReceiver.java new file mode 100644 index 000000000000..9a346ac93a8d --- /dev/null +++ b/core/java/com/android/internal/util/SyncResultReceiver.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2018 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.internal.util; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Bundle; +import android.os.Parcelable; +import android.os.RemoteException; + +import com.android.internal.os.IResultReceiver; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * A {@code IResultReceiver} implementation that can be used to make "sync" Binder calls by blocking + * until it receives a result + * + * @hide + */ +public final class SyncResultReceiver extends IResultReceiver.Stub { + + private static final String EXTRA = "EXTRA"; + + private final CountDownLatch mLatch = new CountDownLatch(1); + private final int mTimeoutMs; + private int mResult; + private Bundle mBundle; + + /** + * Default constructor. + * + * @param timeoutMs how long to block waiting for {@link IResultReceiver} callbacks. + */ + public SyncResultReceiver(int timeoutMs) { + mTimeoutMs = timeoutMs; + } + + private void waitResult() throws TimeoutException { + try { + if (!mLatch.await(mTimeoutMs, TimeUnit.MILLISECONDS)) { + throw new TimeoutException("Not called in " + mTimeoutMs + "ms"); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new TimeoutException("Interrupted"); + } + } + + /** + * Gets the result from an operation that returns an {@code int}. + */ + public int getIntResult() throws TimeoutException { + waitResult(); + return mResult; + } + + /** + * Gets the result from an operation that returns an {@code String}. + */ + @Nullable + public String getStringResult() throws TimeoutException { + waitResult(); + return mBundle == null ? null : mBundle.getString(EXTRA); + } + + /** + * Gets the result from an operation that returns a {@code String[]}. + */ + @Nullable + public String[] getStringArrayResult() throws TimeoutException { + waitResult(); + return mBundle == null ? null : mBundle.getStringArray(EXTRA); + } + + /** + * Gets the result from an operation that returns a {@code Parcelable}. + */ + @Nullable + public <P extends Parcelable> P getParcelableResult() throws TimeoutException { + waitResult(); + return mBundle == null ? null : mBundle.getParcelable(EXTRA); + } + + @Override + public void send(int resultCode, Bundle resultData) { + mResult = resultCode; + mBundle = resultData; + mLatch.countDown(); + } + + /** + * Creates a bundle for a {@code String} value so it can be retrieved by + * {@link #getStringResult()}. + */ + @NonNull + public static Bundle bundleFor(@Nullable String value) { + final Bundle bundle = new Bundle(); + bundle.putString(EXTRA, value); + return bundle; + } + + /** + * Creates a bundle for a {@code String[]} value so it can be retrieved by + * {@link #getStringArrayResult()}. + */ + @NonNull + public static Bundle bundleFor(@Nullable String[] value) { + final Bundle bundle = new Bundle(); + bundle.putStringArray(EXTRA, value); + return bundle; + } + + /** + * Creates a bundle for a {@code Parcelable} value so it can be retrieved by + * {@link #getParcelableResult()}. + */ + @NonNull + public static Bundle bundleFor(@Nullable Parcelable value) { + final Bundle bundle = new Bundle(); + bundle.putParcelable(EXTRA, value); + return bundle; + } + + /** @hide */ + public static final class TimeoutException extends RemoteException { + private TimeoutException(String msg) { + super(msg); + } + } +} diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 6576587899be..f292d25c15fe 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -438,8 +438,9 @@ static jobject nativeGetDisplayedContentSamplingAttributes(JNIEnv* env, jclass c static jboolean nativeSetDisplayedContentSamplingEnabled(JNIEnv* env, jclass clazz, jobject tokenObj, jboolean enable, jint componentMask, jint maxFrames) { sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); - return SurfaceComposerClient::setDisplayContentSamplingEnabled( + status_t rc = SurfaceComposerClient::setDisplayContentSamplingEnabled( token, enable, componentMask, maxFrames); + return rc == OK; } static jobject nativeGetDisplayedContentSample(JNIEnv* env, jclass clazz, jobject tokenObj, @@ -916,6 +917,17 @@ static jlong nativeReadFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) return reinterpret_cast<jlong>(surface.get()); } +static jlong nativeCopyFromSurfaceControl(JNIEnv* env, jclass clazz, jlong surfaceControlNativeObj) { + sp<SurfaceControl> surface(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj)); + if (surface == nullptr) { + return 0; + } + + sp<SurfaceControl> newSurface = new SurfaceControl(surface); + newSurface->incStrong((void *)nativeCreate); + return reinterpret_cast<jlong>(newSurface.get()); +} + static void nativeWriteToParcel(JNIEnv* env, jclass clazz, jlong nativeObject, jobject parcelObj) { Parcel* parcel = parcelForJavaObject(env, parcelObj); @@ -924,7 +936,9 @@ static void nativeWriteToParcel(JNIEnv* env, jclass clazz, return; } SurfaceControl* const self = reinterpret_cast<SurfaceControl *>(nativeObject); - self->writeToParcel(parcel); + if (self != nullptr) { + self->writeToParcel(parcel); + } } // ---------------------------------------------------------------------------- @@ -934,6 +948,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeCreate }, {"nativeReadFromParcel", "(Landroid/os/Parcel;)J", (void*)nativeReadFromParcel }, + {"nativeCopyFromSurfaceControl", "(J)J" , + (void*)nativeCopyFromSurfaceControl }, {"nativeWriteToParcel", "(JLandroid/os/Parcel;)V", (void*)nativeWriteToParcel }, {"nativeRelease", "(J)V", diff --git a/core/jni/android_view_SurfaceSession.cpp b/core/jni/android_view_SurfaceSession.cpp index 30c00302eefd..191f748d80bf 100644 --- a/core/jni/android_view_SurfaceSession.cpp +++ b/core/jni/android_view_SurfaceSession.cpp @@ -46,13 +46,6 @@ static jlong nativeCreate(JNIEnv* env, jclass clazz) { return reinterpret_cast<jlong>(client); } -static jlong nativeCreateScoped(JNIEnv* env, jclass clazz, jlong surfaceObject) { - Surface *parent = reinterpret_cast<Surface*>(surfaceObject); - SurfaceComposerClient* client = new SurfaceComposerClient(parent->getIGraphicBufferProducer()); - client->incStrong((void*)nativeCreate); - return reinterpret_cast<jlong>(client); -} - static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) { SurfaceComposerClient* client = reinterpret_cast<SurfaceComposerClient*>(ptr); client->decStrong((void*)nativeCreate); @@ -67,8 +60,6 @@ static const JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ { "nativeCreate", "()J", (void*)nativeCreate }, - { "nativeCreateScoped", "(J)J", - (void*)nativeCreateScoped }, { "nativeDestroy", "(J)V", (void*)nativeDestroy }, { "nativeKill", "(J)V", diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index a914369a036c..d817aa33302d 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -558,6 +558,8 @@ message GlobalSettingsProto { // ringer mode. optional SettingProto mode_ringer = 75 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto apply_ramping_ringer = 147 [ (android.privacy).dest = DEST_AUTOMATIC ]; + message MultiSim { option (android.msg_privacy).dest = DEST_EXPLICIT; @@ -1017,5 +1019,5 @@ message GlobalSettingsProto { // Please insert fields in alphabetical order and group them into messages // if possible (to avoid reaching the method limit). - // Next tag = 147; + // Next tag = 148; } diff --git a/core/res/res/drawable/ic_account_circle.xml b/core/res/res/drawable/ic_account_circle.xml index f7317dbc5245..71691add7322 100644 --- a/core/res/res/drawable/ic_account_circle.xml +++ b/core/res/res/drawable/ic_account_circle.xml @@ -14,18 +14,14 @@ Copyright (C) 2014 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="48.0dp" - android:height="48.0dp" - android:viewportWidth="20.0" - android:viewportHeight="20.0"> - <group - android:translateX="-2" - android:translateY="-2" > - <path - android:pathData="M12,2C6.48,2 2,6.48 2,12c0,5.52 4.48,10 10,10c5.52,0 10,-4.48 10,-10C22,6.48 17.52,2 12,2zM18.36,16.83c-1.43,-1.74 -4.9,-2.33 -6.36,-2.33s-4.93,0.59 -6.36,2.33C4.62,15.49 4,13.82 4,12c0,-4.41 3.59,-8 8,-8c4.41,0 8,3.59 8,8C20,13.82 19.38,15.49 18.36,16.83z" - android:fillColor="#FFFFFFFF" /> - <path - android:pathData="M12,6c-1.94,0 -3.5,1.56 -3.5,3.5S10.06,13 12,13c1.94,0 3.5,-1.56 3.5,-3.5S13.94,6 12,6z" - android:fillColor="#FFFFFFFF" /> - </group> + android:width="48dp" + android:height="48dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M12,2C6.48,2 2,6.48 2,12c0,5.52 4.48,10 10,10c5.52,0 10,-4.48 10,-10C22,6.48 17.52,2 12,2zM18.36,16.83c-1.43,-1.74 -4.9,-2.33 -6.36,-2.33s-4.93,0.59 -6.36,2.33C4.62,15.49 4,13.82 4,12c0,-4.41 3.59,-8 8,-8c4.41,0 8,3.59 8,8C20,13.82 19.38,15.49 18.36,16.83z"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M12,6c-1.94,0 -3.5,1.56 -3.5,3.5S10.06,13 12,13c1.94,0 3.5,-1.56 3.5,-3.5S13.94,6 12,6z"/> </vector> diff --git a/core/res/res/drawable/ic_battery.xml b/core/res/res/drawable/ic_battery.xml new file mode 100644 index 000000000000..bd40f4df505e --- /dev/null +++ b/core/res/res/drawable/ic_battery.xml @@ -0,0 +1,25 @@ +<!-- + Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?android:attr/colorControlNormal"> + <path + android:fillColor="#FF000000" + android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v15.33C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V5.33C17,4.6 16.4,4 15.67,4z"/> +</vector> diff --git a/core/res/res/drawable/ic_corp_badge.xml b/core/res/res/drawable/ic_corp_badge.xml index 1dad9776851e..5ab504528366 100644 --- a/core/res/res/drawable/ic_corp_badge.xml +++ b/core/res/res/drawable/ic_corp_badge.xml @@ -15,22 +15,11 @@ Copyright (C) 2018 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="20dp" - android:height="20dp" - android:viewportWidth="48" - android:viewportHeight="48"> - + android:width="48dp" + android:height="48dp" + android:viewportWidth="24" + android:viewportHeight="24"> <path - android:fillType="evenOdd" - android:strokeColor="#1A73E8" - android:strokeWidth="4" - android:pathData="M 24 2 C 36.1502644963 2 46 11.8497355037 46 24 C 46 36.1502644963 36.1502644963 46 24 46 C 11.8497355037 46 2 36.1502644963 2 24 C 2 11.8497355037 11.8497355037 2 24 2 Z" /> - <group - android:translateX="10.400000" - android:translateY="10.400000"> - <path - android:fillColor="#1A73E8" - android:strokeWidth="1" - android:pathData="M24.2971429,5.38947368 L18.9485714,5.38947368 L18.9485714,2.80902256 C18.9485714,1.37687218 17.7585143,0.228571429 16.2742857,0.228571429 L10.9257143,0.228571429 C9.44148571,0.228571429 8.25142857,1.37687218 8.25142857,2.80902256 L8.25142857,5.38947368 L2.90285714,5.38947368 C1.41862857,5.38947368 0.241942857,6.53777444 0.241942857,7.96992481 L0.228571429,22.162406 C0.228571429,23.5945564 1.41862857,24.7428571 2.90285714,24.7428571 L24.2971429,24.7428571 C25.7813714,24.7428571 26.9714286,23.5945564 26.9714286,22.162406 L26.9714286,7.96992481 C26.9714286,6.53777444 25.7813714,5.38947368 24.2971429,5.38947368 Z M13.6,17.0015038 C12.1291429,17.0015038 10.9257143,15.8403008 10.9257143,14.4210526 C10.9257143,13.0018045 12.1291429,11.8406015 13.6,11.8406015 C15.0708571,11.8406015 16.2742857,13.0018045 16.2742857,14.4210526 C16.2742857,15.8403008 15.0708571,17.0015038 13.6,17.0015038 Z M16.2742857,5.38947368 L10.9257143,5.38947368 L10.9257143,2.80902256 L16.2742857,2.80902256 L16.2742857,5.38947368 Z" /> - </group> + android:fillColor="@*android:color/accent_device_default_light" + android:pathData="M20,6h-4V4c0,-1.11 -0.89,-2 -2,-2h-4C8.89,2 8,2.89 8,4v2H4C2.89,6 2.01,6.89 2.01,8L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V8C22,6.89 21.11,6 20,6zM10,4h4v2h-4V4zM20,19H4V8h16V19z"/> </vector>
\ No newline at end of file diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 183c2e8cf486..fa3a549463f5 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3660,7 +3660,12 @@ the settings for this service. This setting cannot be changed at runtime. --> <attr name="settingsActivity" /> <!-- Attribute whether the accessibility service wants to be able to retrieve the - active window content. This setting cannot be changed at runtime. --> + active window content. This setting cannot be changed at runtime. + <p> + Required to allow setting the {@link android.accessibilityservice + #AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} flag. + </p> + --> <attr name="canRetrieveWindowContent" format="boolean" /> <!-- Attribute whether the accessibility service wants to be able to request touch exploration mode in which touched items are spoken aloud and the UI can be diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index e4abf8f4cf49..8735557e3f4c 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1169,7 +1169,7 @@ <integer name="config_lowBatteryAutoTriggerDefaultLevel">15</integer> <!-- The app which will handle routine based automatic battery saver, if empty the UI for - routine based battery saver will be hidden --> + routine based battery saver will be hidden --> <string name="config_batterySaverScheduleProvider"></string> <!-- Close low battery warning when battery level reaches the lowBatteryWarningLevel diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index a7bc57afa749..0878562e00fb 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5238,6 +5238,15 @@ <!-- Content description of the overlay icon in the notification. [CHAR LIMIT=NONE] --> <string name="notification_appops_overlay_active">displaying over other apps on your screen</string> + <!-- Dynamic mode battery saver strings --> + <!-- The user visible name of the notification channel for the routine mode battery saver fyi notification [CHAR_LIMIT=80]--> + <string name="dynamic_mode_notification_channel_name">Routine Mode info notification</string> + <!-- Title of notification letting users know why battery saver was turned on automatically [CHAR_LIMIT=NONE]--> + <string name="dynamic_mode_notification_title">Battery may run out before usual charge</string> + <!-- Summary of notification letting users know why battery saver was turned on automatically [CHAR_LIMIT=NONE]--> + <string name="dynamic_mode_notification_summary">Battery Saver activated to extend battery life</string> + + <!-- Strings for car --> <!-- String displayed when loading a user in the car [CHAR LIMIT=30] --> <string name="car_loading_profile">Loading</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index e8cbf666c03c..c2d97bc261e4 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3519,4 +3519,9 @@ <!-- For Secondary Launcher --> <java-symbol type="string" name="config_secondaryHomeComponent" /> + + <java-symbol type="string" name="dynamic_mode_notification_channel_name" /> + <java-symbol type="string" name="dynamic_mode_notification_title" /> + <java-symbol type="string" name="dynamic_mode_notification_summary" /> + <java-symbol type="drawable" name="ic_battery" /> </resources> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index 0f4ca66dcd68..d60313a4402c 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -1454,12 +1454,17 @@ easier. <item name="panelMenuListTheme">@style/Theme.Material.Settings.CompactMenu</item> <!-- action bar --> + <item name="actionBarStyle">@style/Widget.DeviceDefault.Light.ActionBar.Solid</item> <item name="actionBarTheme">@style/ThemeOverlay.DeviceDefault.ActionBar</item> <item name="popupTheme">@style/ThemeOverlay.DeviceDefault.Popup.Light</item> <!-- Color palette --> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorPrimary">@color/primary_device_default_settings_light</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_settings_light</item> <item name="colorSecondary">@color/secondary_device_default_settings_light</item> + <item name="colorAccent">@color/accent_device_default_light</item> + <item name="colorError">@color/error_color_device_default_light</item> <item name="colorEdgeEffect">@android:color/black</item> <!-- Add white nav bar with divider that matches material --> @@ -1467,9 +1472,16 @@ easier. <item name="navigationBarColor">@android:color/white</item> <item name="windowLightNavigationBar">true</item> + <!-- Dialog attributes --> + <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item> + <!-- Button styles --> + <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item> <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item> + <!-- Progress bar attributes --> + <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item> + <item name="listDivider">@color/list_divider_color_light</item> </style> diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index ac57d20a438a..70267c7c2dfa 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -114,6 +114,7 @@ public class SettingsBackupTest { Settings.Global.ANOMALY_CONFIG_VERSION, Settings.Global.APN_DB_UPDATE_CONTENT_URL, Settings.Global.APN_DB_UPDATE_METADATA_URL, + Settings.Global.APPLY_RAMPING_RINGER, Settings.Global.APP_BINDING_CONSTANTS, Settings.Global.APP_IDLE_CONSTANTS, Settings.Global.APP_OPS_CONSTANTS, @@ -269,6 +270,7 @@ public class SettingsBackupTest { Settings.Global.GNSS_SATELLITE_BLACKLIST, Settings.Global.GPRS_REGISTER_CHECK_PERIOD_MS, Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, + Settings.Global.HDMI_CONTROL_AUTO_TV_OFF_ENABLED, Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, Settings.Global.HDMI_CONTROL_ENABLED, Settings.Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java index 81ec85eb2ea5..82eaf88fb07a 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java @@ -368,6 +368,7 @@ public class TextClassifierTest { assertThat(textLanguage, isTextLanguage("ja")); } + /* DISABLED: b/122467291 @Test public void testSuggestConversationActions_textReplyOnly_maxThree() { if (isTextClassifierDisabled()) return; @@ -395,7 +396,7 @@ public class TextClassifierTest { assertThat(conversationAction, isConversationAction(ConversationActions.TYPE_TEXT_REPLY)); } - } + }*/ @Test public void testSuggestConversationActions_textReplyOnly_noMax() { diff --git a/core/tests/hdmitests/Android.mk b/core/tests/hdmitests/Android.mk index e0d2c09a6f0f..2ca31a6a240a 100644 --- a/core/tests/hdmitests/Android.mk +++ b/core/tests/hdmitests/Android.mk @@ -20,7 +20,7 @@ LOCAL_MODULE_TAGS := tests # Include all test java files LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_STATIC_JAVA_LIBRARIES := android-support-test frameworks-base-testutils +LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules frameworks-base-testutils LOCAL_JAVA_LIBRARIES := android.test.runner LOCAL_PACKAGE_NAME := HdmiCecTests diff --git a/core/tests/hdmitests/AndroidManifest.xml b/core/tests/hdmitests/AndroidManifest.xml index 1460b4168581..f8ed118d2338 100644 --- a/core/tests/hdmitests/AndroidManifest.xml +++ b/core/tests/hdmitests/AndroidManifest.xml @@ -22,7 +22,7 @@ <uses-library android:name="android.test.runner" /> </application> - <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="android.hardware.hdmi" android:label="HDMI CEC Tests"/> diff --git a/core/tests/hdmitests/AndroidTest.xml b/core/tests/hdmitests/AndroidTest.xml index 7ef672d344c5..0c8da28cab7c 100644 --- a/core/tests/hdmitests/AndroidTest.xml +++ b/core/tests/hdmitests/AndroidTest.xml @@ -29,6 +29,6 @@ <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="android.hardware.hdmi" /> <option name="hidden-api-checks" value="false"/> - <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/> </test> </configuration>
\ No newline at end of file diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java index 7b76a0819e22..0dd9d0904746 100644 --- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java +++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java @@ -18,15 +18,18 @@ package android.hardware.hdmi; import android.os.Handler; import android.os.test.TestLooper; -import android.support.test.filters.SmallTest; import android.util.Log; -import java.util.List; + +import androidx.test.filters.SmallTest; + import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.util.List; + /** * Tests for {@link HdmiAudioSystemClient} */ diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java index 2227cf5ef2e0..f0efb581dab6 100644 --- a/graphics/java/android/graphics/ColorSpace.java +++ b/graphics/java/android/graphics/ColorSpace.java @@ -1354,9 +1354,9 @@ public abstract class ColorSpace { */ @NonNull static ColorSpace get(@IntRange(from = MIN_ID, to = MAX_ID) int index) { - if (index < 0 || index > Named.values().length) { + if (index < 0 || index >= Named.values().length) { throw new IllegalArgumentException("Invalid ID, must be in the range [0.." + - Named.values().length + "]"); + Named.values().length + ")"); } return sNamedColorSpaces[index]; } diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp index 5b7ae70e821c..9170d6d1dc50 100644 --- a/libs/hwui/WebViewFunctorManager.cpp +++ b/libs/hwui/WebViewFunctorManager.cpp @@ -18,6 +18,7 @@ #include <private/hwui/WebViewFunctor.h> #include "Properties.h" +#include "renderthread/RenderThread.h" #include <log/log.h> #include <utils/Trace.h> @@ -90,6 +91,10 @@ void WebViewFunctor::destroyContext() { mHasContext = false; ATRACE_NAME("WebViewFunctor::onContextDestroyed"); mCallbacks.onContextDestroyed(mFunctor, mData); + + // grContext may be null in unit tests. + auto* grContext = renderthread::RenderThread::getInstance().getGrContext(); + if (grContext) grContext->resetContext(); } } diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 7acc44ca65b7..6c04232ab7f5 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -30,6 +30,7 @@ #include <gui/Surface.h> #include <math.h> #include <set> +#include <SkMathPriv.h> namespace android { namespace uirenderer { @@ -42,11 +43,6 @@ namespace renderthread { #define SURFACE_SIZE_MULTIPLIER (12.0f * 4.0f) #define BACKGROUND_RETENTION_PERCENTAGE (0.5f) -// for super large fonts we will draw them as paths so no need to keep linearly -// increasing the font cache size. -#define FONT_CACHE_MIN_MB (0.5f) -#define FONT_CACHE_MAX_MB (4.0f) - CacheManager::CacheManager(const DisplayInfo& display) : mMaxSurfaceArea(display.w * display.h) { mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas( mMaxSurfaceArea / 2, @@ -106,25 +102,10 @@ public: void CacheManager::configureContext(GrContextOptions* contextOptions, const void* identity, ssize_t size) { contextOptions->fAllowPathMaskCaching = true; - float screenMP = mMaxSurfaceArea / 1024.0f / 1024.0f; - float fontCacheMB = 0; - float decimalVal = std::modf(screenMP, &fontCacheMB); - - // This is a basic heuristic to size the cache to a multiple of 512 KB - if (decimalVal > 0.8f) { - fontCacheMB += 1.0f; - } else if (decimalVal > 0.5f) { - fontCacheMB += 0.5f; - } - - // set limits on min/max size of the cache - fontCacheMB = std::max(FONT_CACHE_MIN_MB, std::min(FONT_CACHE_MAX_MB, fontCacheMB)); - - // We must currently set the size of the text cache based on the size of the - // display even though we like to be dynamicallysizing it to the size of the window. - // Skia's implementation doesn't provide a mechanism to resize the font cache due to - // the potential cost of recreating the glyphs. - contextOptions->fGlyphCacheTextureMaximumBytes = fontCacheMB * 1024 * 1024; + // This sets the maximum size for a single texture atlas in the GPU font cache. If necessary, + // the cache can allocate additional textures that are counted against the total cache limits + // provided to Skia. + contextOptions->fGlyphCacheTextureMaximumBytes = GrNextSizePow2(mMaxSurfaceArea); if (mTaskManager.canRunTasks()) { if (!mTaskProcessor.get()) { diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index 12666b323d11..5272227509c8 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -123,6 +123,7 @@ private: friend class RenderProxy; friend class DummyVsyncSource; friend class android::uirenderer::TestUtils; + friend class android::uirenderer::WebViewFunctor; RenderThread(); virtual ~RenderThread(); diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp index 3d50d2d7e59c..29e4256e2ab7 100644 --- a/libs/hwui/service/GraphicsStatsService.cpp +++ b/libs/hwui/service/GraphicsStatsService.cpp @@ -139,6 +139,7 @@ bool GraphicsStatsService::parseFromFile(const std::string& path, uint32_t file_version = *reinterpret_cast<uint32_t*>(addr); if (file_version != sCurrentFileVersion) { ALOGW("file_version mismatch! expected %d got %d", sCurrentFileVersion, file_version); + munmap(addr, sb.st_size); return false; } @@ -150,6 +151,7 @@ bool GraphicsStatsService::parseFromFile(const std::string& path, ALOGW("Parse failed on '%s' error='%s'", path.c_str(), output->InitializationErrorString().c_str()); } + munmap(addr, sb.st_size); return success; } diff --git a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp index 1d3d60716d68..5af7d43d7f66 100644 --- a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp +++ b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp @@ -31,7 +31,7 @@ static bool _BitmapFillrate( class BitmapFillrate : public TestScene { public: - BitmapFillrate(BitmapAllocationTestUtils::BitmapAllocator allocator) + explicit BitmapFillrate(BitmapAllocationTestUtils::BitmapAllocator allocator) : TestScene(), mAllocator(allocator) {} void createContent(int width, int height, Canvas& canvas) override { @@ -70,4 +70,4 @@ private: BitmapAllocationTestUtils::BitmapAllocator mAllocator; std::vector<sp<RenderNode> > mNodes; -};
\ No newline at end of file +}; diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp index ad11a1d32310..510766073b08 100644 --- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp +++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp @@ -26,7 +26,7 @@ static bool _BitmapShaders(BitmapAllocationTestUtils::registerBitmapAllocationSc class BitmapShaders : public TestScene { public: - BitmapShaders(BitmapAllocationTestUtils::BitmapAllocator allocator) + explicit BitmapShaders(BitmapAllocationTestUtils::BitmapAllocator allocator) : TestScene(), mAllocator(allocator) {} sp<RenderNode> card; diff --git a/libs/hwui/tests/common/scenes/TvApp.cpp b/libs/hwui/tests/common/scenes/TvApp.cpp index a64e8444a9b1..286f5f194aed 100644 --- a/libs/hwui/tests/common/scenes/TvApp.cpp +++ b/libs/hwui/tests/common/scenes/TvApp.cpp @@ -48,7 +48,7 @@ static bool _TvAppNoRoundedCornerColorFilter( class TvApp : public TestScene { public: - TvApp(BitmapAllocationTestUtils::BitmapAllocator allocator) + explicit TvApp(BitmapAllocationTestUtils::BitmapAllocator allocator) : TestScene(), mAllocator(allocator) {} sp<RenderNode> mBg; @@ -232,7 +232,7 @@ private: class TvAppNoRoundedCorner : public TvApp { public: - TvAppNoRoundedCorner(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {} + explicit TvAppNoRoundedCorner(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {} private: virtual float roundedCornerRadius() override { return dp(0); } @@ -240,7 +240,7 @@ private: class TvAppColorFilter : public TvApp { public: - TvAppColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {} + explicit TvAppColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {} private: virtual bool useOverlay() override { return false; } @@ -248,7 +248,7 @@ private: class TvAppNoRoundedCornerColorFilter : public TvApp { public: - TvAppNoRoundedCornerColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator) + explicit TvAppNoRoundedCornerColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {} private: diff --git a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp index 479c462bd1a6..635429dea359 100644 --- a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp +++ b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp @@ -44,7 +44,7 @@ static void testProperty(std::function<void(RenderProperties&)> propSetupCallbac static const int CANVAS_HEIGHT = 100; class PropertyTestCanvas : public TestCanvasBase { public: - PropertyTestCanvas(std::function<void(const SkCanvas&)> callback) + explicit PropertyTestCanvas(std::function<void(const SkCanvas&)> callback) : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT), mCallback(callback) {} void onDrawRect(const SkRect& rect, const SkPaint& paint) override { EXPECT_EQ(mDrawCounter++, 0); diff --git a/libs/hwui/tests/unit/ThreadBaseTests.cpp b/libs/hwui/tests/unit/ThreadBaseTests.cpp index 1168ff211202..817c1f3d3e43 100644 --- a/libs/hwui/tests/unit/ThreadBaseTests.cpp +++ b/libs/hwui/tests/unit/ThreadBaseTests.cpp @@ -95,7 +95,7 @@ TEST(ThreadBase, lifecyclePerf) { }; struct Counter { - Counter(EventCount* count) : mCount(count) { mCount->construct++; } + explicit Counter(EventCount* count) : mCount(count) { mCount->construct++; } Counter(const Counter& other) : mCount(other.mCount) { if (mCount) mCount->copy++; @@ -148,4 +148,4 @@ TEST(ThreadBase, lifecycle) { ASSERT_EQ(1, dummyObject->getStrongCount()); ASSERT_EQ(2, lifecycleTestHelper(dummyObject)); ASSERT_EQ(1, dummyObject->getStrongCount()); -}
\ No newline at end of file +} diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index b16002900799..eeb146151ff1 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -456,6 +456,9 @@ public class MediaRecorder implements AudioRouting, /** VP8/VORBIS data in a WEBM container */ public static final int WEBM = 9; + /** @hide HEIC data in a HEIF container */ + public static final int HEIF = 10; + /** Opus data in a Ogg container */ public static final int OGG = 11; }; diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl index e61bf5b48223..a843881e95ae 100644 --- a/media/java/android/media/session/ISessionController.aidl +++ b/media/java/android/media/session/ISessionController.aidl @@ -21,8 +21,8 @@ import android.content.pm.ParceledListSlice; import android.media.MediaMetadata; import android.media.Rating; import android.media.session.ISessionControllerCallback; +import android.media.session.MediaController; import android.media.session.MediaSession; -import android.media.session.ParcelableVolumeInfo; import android.media.session.PlaybackState; import android.net.Uri; import android.os.Bundle; @@ -47,7 +47,7 @@ interface ISessionController { String getTag(); PendingIntent getLaunchPendingIntent(); long getFlags(); - ParcelableVolumeInfo getVolumeAttributes(); + MediaController.PlaybackInfo getVolumeAttributes(); void adjustVolume(String packageName, String opPackageName, in ISessionControllerCallback caller, boolean asSystemService, int direction, int flags); diff --git a/media/java/android/media/session/ISessionControllerCallback.aidl b/media/java/android/media/session/ISessionControllerCallback.aidl index cf3176706d7e..fac8897d1b89 100644 --- a/media/java/android/media/session/ISessionControllerCallback.aidl +++ b/media/java/android/media/session/ISessionControllerCallback.aidl @@ -17,8 +17,8 @@ package android.media.session; import android.content.pm.ParceledListSlice; import android.media.MediaMetadata; -import android.media.session.ParcelableVolumeInfo; import android.media.session.PlaybackState; +import android.media.session.MediaController; import android.media.session.MediaSession; import android.os.Bundle; @@ -35,5 +35,5 @@ oneway interface ISessionControllerCallback { void onQueueChanged(in ParceledListSlice queue); void onQueueTitleChanged(CharSequence title); void onExtrasChanged(in Bundle extras); - void onVolumeInfoChanged(in ParcelableVolumeInfo info); + void onVolumeInfoChanged(in MediaController.PlaybackInfo info); } diff --git a/media/java/android/media/session/ParcelableVolumeInfo.aidl b/media/java/android/media/session/MediaController.aidl index c4250f097fb4..17167f45d0e3 100644 --- a/media/java/android/media/session/ParcelableVolumeInfo.aidl +++ b/media/java/android/media/session/MediaController.aidl @@ -15,4 +15,4 @@ package android.media.session; -parcelable ParcelableVolumeInfo; +parcelable MediaController.PlaybackInfo; diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java index 181ee5327547..ef2df15da8f4 100644 --- a/media/java/android/media/session/MediaController.java +++ b/media/java/android/media/session/MediaController.java @@ -32,6 +32,8 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.Parcel; +import android.os.Parcelable; import android.os.RemoteException; import android.os.ResultReceiver; import android.text.TextUtils; @@ -327,10 +329,7 @@ public final class MediaController { */ public @Nullable PlaybackInfo getPlaybackInfo() { try { - ParcelableVolumeInfo result = mSessionBinder.getVolumeAttributes(); - return new PlaybackInfo(result.volumeType, result.audioAttrs, result.controlType, - result.maxVolume, result.currentVolume); - + return mSessionBinder.getVolumeAttributes(); } catch (RemoteException e) { Log.wtf(TAG, "Error calling getAudioInfo.", e); } @@ -986,15 +985,15 @@ public final class MediaController { * Holds information about the current playback and how audio is handled for * this session. */ - public static final class PlaybackInfo { - /** - * The session uses remote playback. - */ - public static final int PLAYBACK_TYPE_REMOTE = 2; + public static final class PlaybackInfo implements Parcelable { /** * The session uses local playback. */ public static final int PLAYBACK_TYPE_LOCAL = 1; + /** + * The session uses remote playback. + */ + public static final int PLAYBACK_TYPE_REMOTE = 2; private final int mVolumeType; private final int mVolumeControl; @@ -1005,12 +1004,20 @@ public final class MediaController { /** * @hide */ - public PlaybackInfo(int type, AudioAttributes attrs, int control, int max, int current) { + public PlaybackInfo(int type, int control, int max, int current, AudioAttributes attrs) { mVolumeType = type; - mAudioAttrs = attrs; mVolumeControl = control; mMaxVolume = max; mCurrentVolume = current; + mAudioAttrs = attrs; + } + + PlaybackInfo(Parcel in) { + mVolumeType = in.readInt(); + mVolumeControl = in.readInt(); + mMaxVolume = in.readInt(); + mCurrentVolume = in.readInt(); + mAudioAttrs = in.readParcelable(null); } /** @@ -1027,18 +1034,6 @@ public final class MediaController { } /** - * Get the audio attributes for this session. The attributes will affect - * volume handling for the session. When the volume type is - * {@link PlaybackInfo#PLAYBACK_TYPE_REMOTE} these may be ignored by the - * remote volume handler. - * - * @return The attributes for this session. - */ - public AudioAttributes getAudioAttributes() { - return mAudioAttrs; - } - - /** * Get the type of volume control that can be used. One of: * <ul> * <li>{@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}</li> @@ -1070,6 +1065,52 @@ public final class MediaController { public int getCurrentVolume() { return mCurrentVolume; } + + /** + * Get the audio attributes for this session. The attributes will affect + * volume handling for the session. When the volume type is + * {@link PlaybackInfo#PLAYBACK_TYPE_REMOTE} these may be ignored by the + * remote volume handler. + * + * @return The attributes for this session. + */ + public AudioAttributes getAudioAttributes() { + return mAudioAttrs; + } + + @Override + public String toString() { + return "volumeType=" + mVolumeType + ", volumeControl=" + mVolumeControl + + ", maxVolume=" + mMaxVolume + ", currentVolume=" + mCurrentVolume + + ", audioAttrs=" + mAudioAttrs; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mVolumeType); + dest.writeInt(mVolumeControl); + dest.writeInt(mMaxVolume); + dest.writeInt(mCurrentVolume); + dest.writeParcelable(mAudioAttrs, flags); + } + + public static final Parcelable.Creator<PlaybackInfo> CREATOR = + new Parcelable.Creator<PlaybackInfo>() { + @Override + public PlaybackInfo createFromParcel(Parcel in) { + return new PlaybackInfo(in); + } + + @Override + public PlaybackInfo[] newArray(int size) { + return new PlaybackInfo[size]; + } + }; } private final static class CallbackStub extends ISessionControllerCallback.Stub { @@ -1138,11 +1179,9 @@ public final class MediaController { } @Override - public void onVolumeInfoChanged(ParcelableVolumeInfo pvi) { + public void onVolumeInfoChanged(PlaybackInfo info) { MediaController controller = mController.get(); if (controller != null) { - PlaybackInfo info = new PlaybackInfo(pvi.volumeType, pvi.audioAttrs, - pvi.controlType, pvi.maxVolume, pvi.currentVolume); controller.postMessage(MSG_UPDATE_VOLUME, info, null); } } @@ -1154,7 +1193,7 @@ public final class MediaController { private boolean mRegistered = false; public MessageHandler(Looper looper, MediaController.Callback cb) { - super(looper, null, true); + super(looper); mCallback = cb; } @@ -1193,6 +1232,7 @@ public final class MediaController { public void post(int what, Object obj, Bundle data) { Message msg = obtainMessage(what, obj); + msg.setAsynchronous(true); msg.setData(data); msg.sendToTarget(); } diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 8962bb7fb4c4..df8cc35945d3 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -1453,7 +1453,7 @@ public final class MediaSession { private RemoteUserInfo mCurrentControllerInfo; public CallbackMessageHandler(Looper looper, MediaSession.Callback callback) { - super(looper, null, true); + super(looper); mCallback = callback; mCallback.mHandler = this; } @@ -1461,6 +1461,7 @@ public final class MediaSession { public void post(RemoteUserInfo caller, int what, Object obj, Bundle data, long delayMs) { Pair<RemoteUserInfo, Object> objWithCaller = Pair.create(caller, obj); Message msg = obtainMessage(what, objWithCaller); + msg.setAsynchronous(true); msg.setData(data); if (delayMs > 0) { sendMessageDelayed(msg, delayMs); diff --git a/media/java/android/media/session/ParcelableVolumeInfo.java b/media/java/android/media/session/ParcelableVolumeInfo.java deleted file mode 100644 index f59c9756d466..000000000000 --- a/media/java/android/media/session/ParcelableVolumeInfo.java +++ /dev/null @@ -1,80 +0,0 @@ -/* Copyright 2014, The Android Open Source Project - ** - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** - ** http://www.apache.org/licenses/LICENSE-2.0 - ** - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - */ - -package android.media.session; - -import android.media.AudioAttributes; -import android.os.Parcel; -import android.os.Parcelable; - -/** - * Convenience class for passing information about the audio configuration of a - * session. The public implementation is {@link MediaController.PlaybackInfo}. - * - * @hide - */ -public class ParcelableVolumeInfo implements Parcelable { - public int volumeType; - public AudioAttributes audioAttrs; - public int controlType; - public int maxVolume; - public int currentVolume; - - public ParcelableVolumeInfo(int volumeType, AudioAttributes audioAttrs, int controlType, - int maxVolume, - int currentVolume) { - this.volumeType = volumeType; - this.audioAttrs = audioAttrs; - this.controlType = controlType; - this.maxVolume = maxVolume; - this.currentVolume = currentVolume; - } - - public ParcelableVolumeInfo(Parcel from) { - volumeType = from.readInt(); - controlType = from.readInt(); - maxVolume = from.readInt(); - currentVolume = from.readInt(); - audioAttrs = AudioAttributes.CREATOR.createFromParcel(from); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(volumeType); - dest.writeInt(controlType); - dest.writeInt(maxVolume); - dest.writeInt(currentVolume); - audioAttrs.writeToParcel(dest, flags); - } - - - public static final Parcelable.Creator<ParcelableVolumeInfo> CREATOR - = new Parcelable.Creator<ParcelableVolumeInfo>() { - @Override - public ParcelableVolumeInfo createFromParcel(Parcel in) { - return new ParcelableVolumeInfo(in); - } - - @Override - public ParcelableVolumeInfo[] newArray(int size) { - return new ParcelableVolumeInfo[size]; - } - }; -} diff --git a/media/jni/Android.bp b/media/jni/Android.bp index f75f69b4f2ee..fda0fc4c8546 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -102,7 +102,7 @@ cc_library_shared { "libhidlbase", "libhidlmemory", - "libpowermanager", // Used by JWakeLock. Will be replace with public SDJ API. + "libpowermanager", // Used by JWakeLock. Will be replace with public SDK API. "libmediametrics", // Used by MediaMetrics. Will be replaced with stable C API. "libbinder", // Used by JWakeLock and MediaMetrics. @@ -111,6 +111,7 @@ cc_library_shared { // NDK or NDK-compliant "libandroid", + "libbinder_ndk", "libmediandk", "libnativehelper_compat_libc++", "liblog", diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java index 62502ef60e96..6d960d7b9e21 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java @@ -18,8 +18,8 @@ package com.android.systemui.car; import android.content.Context; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; /** Auto-specific implementation of {@link NotificationInterruptionStateProvider}. */ public class CarNotificationInterruptionStateProvider extends @@ -29,7 +29,7 @@ public class CarNotificationInterruptionStateProvider extends } @Override - public boolean shouldHeadsUp(NotificationData.Entry entry) { + public boolean shouldHeadsUp(NotificationEntry entry) { // Because space is usually constrained in the auto use-case, there should not be a // pinned notification when the shade has been expanded. Ensure this by not pinning any // notification if the shade is already opened. diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java index eed66e94a9ef..d332bac3bb6d 100644 --- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java +++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java @@ -77,7 +77,7 @@ import java.util.Arrays; */ public class BarChartPreference extends Preference { - static final int MAXIMUM_BAR_VIEWS = 4; + public static final int MAXIMUM_BAR_VIEWS = 4; private static final String TAG = "BarChartPreference"; private static final int[] BAR_VIEWS = { R.id.bar_view1, diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 570d351a8b71..27d624ab0b58 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -16,6 +16,8 @@ import com.android.systemui.Dependency; import com.android.systemui.plugins.ClockPlugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.StatusBarStateController; import java.util.Objects; import java.util.TimeZone; @@ -82,6 +84,24 @@ public class KeyguardClockSwitch extends RelativeLayout { } } }; + private final StatusBarStateController.StateListener mStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onStateChanged(int newState) { + if (mBigClockContainer == null) { + return; + } + if (newState == StatusBarState.SHADE) { + if (mBigClockContainer.getVisibility() == View.VISIBLE) { + mBigClockContainer.setVisibility(View.INVISIBLE); + } + } else { + if (mBigClockContainer.getVisibility() == View.INVISIBLE) { + mBigClockContainer.setVisibility(View.VISIBLE); + } + } + } + }; public KeyguardClockSwitch(Context context) { this(context, null); @@ -104,12 +124,14 @@ public class KeyguardClockSwitch extends RelativeLayout { super.onAttachedToWindow(); Dependency.get(PluginManager.class).addPluginListener(mClockPluginListener, ClockPlugin.class); + Dependency.get(StatusBarStateController.class).addCallback(mStateListener); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); Dependency.get(PluginManager.class).removePluginListener(mClockPluginListener); + Dependency.get(StatusBarStateController.class).removeCallback(mStateListener); } /** @@ -238,4 +260,9 @@ public class KeyguardClockSwitch extends RelativeLayout { PluginListener getClockPluginListener() { return mClockPluginListener; } + + @VisibleForTesting (otherwise = VisibleForTesting.NONE) + StatusBarStateController.StateListener getStateListener() { + return mStateListener; + } } diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 3cc9bb6f668f..9d810645f6a2 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -56,12 +56,12 @@ import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.NotificationAlertingManager; -import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationFilter; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.NotificationRowBinder; import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java index b0b7e6c88984..96f216e357af 100644 --- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java +++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java @@ -24,9 +24,9 @@ import android.service.notification.StatusBarNotification; import android.util.Log; import com.android.internal.statusbar.NotificationVisibility; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import javax.inject.Inject; import javax.inject.Singleton; @@ -49,18 +49,18 @@ public class ForegroundServiceNotificationListener { mForegroundServiceController = foregroundServiceController; notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() { @Override - public void onPendingEntryAdded(NotificationData.Entry entry) { + public void onPendingEntryAdded(NotificationEntry entry) { addNotification(entry.notification, entry.importance); } @Override - public void onEntryUpdated(NotificationData.Entry entry) { + public void onEntryUpdated(NotificationEntry entry) { updateNotification(entry.notification, entry.importance); } @Override public void onEntryRemoved( - NotificationData.Entry entry, + NotificationEntry entry, NotificationVisibility visibility, boolean removedByUser) { removeNotification(entry.notification); diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 5347a5cb16b6..d0111cb1a5b3 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -40,9 +40,9 @@ import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl; import com.android.systemui.statusbar.ScrimView; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; +import com.android.systemui.statusbar.notification.collection.NotificationData; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBouncer; import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 644723321103..75776098decf 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -35,9 +35,9 @@ import android.widget.FrameLayout; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.phone.StatusBarWindowController; import java.util.ArrayList; @@ -180,7 +180,7 @@ public class BubbleController { /** * Adds a bubble associated with the provided notification entry or updates it if it exists. */ - public void addBubble(NotificationData.Entry notif) { + public void addBubble(NotificationEntry notif) { if (mBubbles.containsKey(notif.key)) { // It's an update BubbleView bubble = mBubbles.get(notif.key); @@ -226,7 +226,7 @@ public class BubbleController { bv.getEntry().setBubbleDismissed(true); } - NotificationData.Entry entry = mNotificationEntryManager.getNotificationData().get(key); + NotificationEntry entry = mNotificationEntryManager.getNotificationData().get(key); if (entry != null) { entry.setBubbleDismissed(true); if (!DEBUG_DEMOTE_TO_NOTIF) { @@ -241,7 +241,7 @@ public class BubbleController { @SuppressWarnings("FieldCanBeLocal") private final NotificationEntryListener mEntryListener = new NotificationEntryListener() { @Override - public void onPendingEntryAdded(NotificationData.Entry entry) { + public void onPendingEntryAdded(NotificationEntry entry) { if (shouldAutoBubble(mContext, entry)) { entry.setIsBubble(true); } @@ -275,7 +275,7 @@ public class BubbleController { } ArrayList<BubbleView> viewsToRemove = new ArrayList<>(); for (BubbleView bv : mBubbles.values()) { - NotificationData.Entry entry = bv.getEntry(); + NotificationEntry entry = bv.getEntry(); if (entry != null) { if (entry.isRowRemoved() || entry.isBubbleDismissed() || entry.isRowDismissed()) { viewsToRemove.add(bv); @@ -332,7 +332,7 @@ public class BubbleController { /** * Whether the notification should bubble or not. */ - private static boolean shouldAutoBubble(Context context, NotificationData.Entry entry) { + private static boolean shouldAutoBubble(Context context, NotificationEntry entry) { if (entry.isBubbleDismissed()) { return false; } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index dfd18b23a5e3..d69e7b3ae924 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -40,7 +40,7 @@ import androidx.annotation.Nullable; import com.android.internal.widget.ViewClippingUtil; import com.android.systemui.R; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.ExpandableViewState; import com.android.systemui.statusbar.notification.stack.ViewState; @@ -247,7 +247,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F * @param bubbleView the view to update in the stack. * @param entry the entry to update it with. */ - public void updateBubble(BubbleView bubbleView, NotificationData.Entry entry) { + public void updateBubble(BubbleView bubbleView, NotificationEntry entry) { // TODO - move to top of bubble stack, make it show its update if it makes sense bubbleView.update(entry); if (bubbleView.equals(mExpandedBubble)) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java index 6c47aac712f6..3307992e779a 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java @@ -31,7 +31,7 @@ import android.widget.LinearLayout; import com.android.internal.util.ContrastColorUtil; import com.android.systemui.R; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; /** @@ -43,7 +43,7 @@ public class BubbleView extends LinearLayout implements BubbleTouchHandler.Float private Context mContext; private View mIconView; - private NotificationData.Entry mEntry; + private NotificationEntry mEntry; private int mBubbleSize; private int mIconSize; @@ -72,7 +72,7 @@ public class BubbleView extends LinearLayout implements BubbleTouchHandler.Float * * @param entry the notification to display as a bubble. */ - public void setNotif(NotificationData.Entry entry) { + public void setNotif(NotificationEntry entry) { removeAllViews(); // TODO: migrate to inflater mIconView = new ImageView(mContext); @@ -89,7 +89,7 @@ public class BubbleView extends LinearLayout implements BubbleTouchHandler.Float /** * Updates the UI based on the entry. */ - public void update(NotificationData.Entry entry) { + public void update(NotificationEntry entry) { mEntry = entry; Notification n = entry.notification.getNotification(); Icon ic = n.getLargeIcon() != null ? n.getLargeIcon() : n.getSmallIcon(); @@ -112,7 +112,7 @@ public class BubbleView extends LinearLayout implements BubbleTouchHandler.Float /** * @return the notification entry associated with this bubble. */ - public NotificationData.Entry getEntry() { + public NotificationEntry getEntry() { return mEntry; } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index 6a0e8ad35736..c4c8bc703802 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -50,9 +50,9 @@ import androidx.slice.builders.SliceAction; import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; -import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.statusbar.NotificationMediaManager; +import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.policy.NextAlarmController; import com.android.systemui.statusbar.policy.NextAlarmControllerImpl; import com.android.systemui.statusbar.policy.ZenModeController; @@ -68,7 +68,7 @@ import java.util.concurrent.TimeUnit; */ public class KeyguardSliceProvider extends SliceProvider implements NextAlarmController.NextAlarmChangeCallback, ZenModeController.Callback, - NotificationMediaManager.MediaListener { + NotificationMediaManager.MediaListener, StatusBarStateController.StateListener { private static final StyleSpan BOLD_STYLE = new StyleSpan(Typeface.BOLD); public static final String KEYGUARD_SLICE_URI = "content://com.android.systemui.keyguard/main"; @@ -109,7 +109,9 @@ public class KeyguardSliceProvider extends SliceProvider implements private AlarmManager.AlarmClockInfo mNextAlarmInfo; private PendingIntent mPendingIntent; protected NotificationMediaManager mMediaManager; + private StatusBarStateController mStatusBarStateController; protected MediaMetadata mMediaMetaData; + protected boolean mDozing; /** * Receiver responsible for time ticking and updating the date format. @@ -167,9 +169,20 @@ public class KeyguardSliceProvider extends SliceProvider implements mMediaUri = Uri.parse(KEYGUARD_MEDIA_URI); } - public void initDependencies() { - mMediaManager = Dependency.get(NotificationMediaManager.class); + /** + * Initialize dependencies that don't exist during {@link android.content.ContentProvider} + * instantiation. + * + * @param mediaManager {@link NotificationMediaManager} singleton. + * @param statusBarStateController {@link StatusBarStateController} singleton. + */ + public void initDependencies( + NotificationMediaManager mediaManager, + StatusBarStateController statusBarStateController) { + mMediaManager = mediaManager; mMediaManager.addCallback(this); + mStatusBarStateController = statusBarStateController; + mStatusBarStateController.addCallback(this); } @AnyThread @@ -179,7 +192,7 @@ public class KeyguardSliceProvider extends SliceProvider implements Slice slice; synchronized (this) { ListBuilder builder = new ListBuilder(getContext(), mSliceUri, ListBuilder.INFINITY); - if (mMediaMetaData != null) { + if (needsMediaLocked()) { addMediaLocked(builder); } else { builder.addRow(new RowBuilder(mDateUri).setTitle(mLastText)); @@ -193,6 +206,10 @@ public class KeyguardSliceProvider extends SliceProvider implements return slice; } + protected boolean needsMediaLocked() { + return mMediaMetaData != null && mDozing; + } + protected void addMediaLocked(ListBuilder listBuilder) { if (mMediaMetaData != null) { SpannableStringBuilder builder = new SpannableStringBuilder(); @@ -209,7 +226,7 @@ public class KeyguardSliceProvider extends SliceProvider implements } RowBuilder mediaBuilder = new RowBuilder(mMediaUri).setTitle(builder); - Icon notificationIcon = mMediaManager.getMediaIcon(); + Icon notificationIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon(); if (notificationIcon != null) { IconCompat icon = IconCompat.createFromIcon(notificationIcon); mediaBuilder.addEndItem(icon, ListBuilder.ICON_IMAGE); @@ -389,13 +406,35 @@ public class KeyguardSliceProvider extends SliceProvider implements @Override public void onMetadataChanged(MediaMetadata metadata) { + final boolean notify; synchronized (this) { + boolean neededMedia = needsMediaLocked(); mMediaMetaData = metadata; + notify = neededMedia != needsMediaLocked(); + } + if (notify) { + notifyChange(); } - notifyChange(); } protected void notifyChange() { mContentResolver.notifyChange(mSliceUri, null /* observer */); } + + @Override + public void onDozingChanged(boolean isDozing) { + final boolean notify; + synchronized (this) { + boolean neededMedia = needsMediaLocked(); + mDozing = isDozing; + notify = neededMedia != needsMediaLocked(); + } + if (notify) { + notifyChange(); + } + } + + @Override + public void onStateChanged(int newState) { + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java index bc381699494a..a776d0fbb45e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar; -import static com.android.systemui.statusbar.notification.NotificationData.Entry; - import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Handler; @@ -29,6 +27,7 @@ import android.util.Log; import android.view.accessibility.AccessibilityEvent; import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag; import java.util.stream.Stream; @@ -48,7 +47,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim * NotificationManagerService side, but we keep it to prevent the UI from looking weird and * will remove when possible. See {@link NotificationLifetimeExtender} */ - protected final ArraySet<Entry> mExtendedLifetimeAlertEntries = new ArraySet<>(); + protected final ArraySet<NotificationEntry> mExtendedLifetimeAlertEntries = new ArraySet<>(); protected NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback; protected int mMinimumDisplayTime; @@ -61,7 +60,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim * Adds the notification to be managed. * @param entry entry to show */ - public void showNotification(@NonNull Entry entry) { + public void showNotification(@NonNull NotificationEntry entry) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "showNotification"); } @@ -139,7 +138,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim * @return the entry */ @Nullable - public Entry getEntry(@NonNull String key) { + public NotificationEntry getEntry(@NonNull String key) { AlertEntry entry = mAlertEntries.get(key); return entry != null ? entry.mEntry : null; } @@ -149,7 +148,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim * @return all entries */ @NonNull - public Stream<Entry> getAllEntries() { + public Stream<NotificationEntry> getAllEntries() { return mAlertEntries.values().stream().map(headsUpEntry -> headsUpEntry.mEntry); } @@ -180,7 +179,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim * Add a new entry and begin managing it. * @param entry the entry to add */ - protected final void addAlertEntry(@NonNull Entry entry) { + protected final void addAlertEntry(@NonNull NotificationEntry entry) { AlertEntry alertEntry = createAlertEntry(); alertEntry.setEntry(entry); mAlertEntries.put(entry.key, alertEntry); @@ -203,7 +202,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim if (alertEntry == null) { return; } - Entry entry = alertEntry.mEntry; + NotificationEntry entry = alertEntry.mEntry; mAlertEntries.remove(key); onAlertEntryRemoved(alertEntry); entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); @@ -250,12 +249,12 @@ public abstract class AlertingNotificationManager implements NotificationLifetim } @Override - public boolean shouldExtendLifetime(Entry entry) { + public boolean shouldExtendLifetime(NotificationEntry entry) { return !canRemoveImmediately(entry.key); } @Override - public void setShouldManageLifetime(Entry entry, boolean shouldExtend) { + public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) { if (shouldExtend) { mExtendedLifetimeAlertEntries.add(entry); } else { @@ -265,17 +264,17 @@ public abstract class AlertingNotificationManager implements NotificationLifetim /////////////////////////////////////////////////////////////////////////////////////////////// protected class AlertEntry implements Comparable<AlertEntry> { - @Nullable public Entry mEntry; + @Nullable public NotificationEntry mEntry; public long mPostTime; public long mEarliestRemovaltime; @Nullable protected Runnable mRemoveAlertRunnable; - public void setEntry(@NonNull final Entry entry) { + public void setEntry(@NonNull final NotificationEntry entry) { setEntry(entry, () -> removeAlertEntry(entry.key)); } - public void setEntry(@NonNull final Entry entry, + public void setEntry(@NonNull final NotificationEntry entry, @Nullable Runnable removeAlertRunnable) { mEntry = entry; mRemoveAlertRunnable = removeAlertRunnable; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java index 9bfd4ee24ff0..a3beb96780d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java @@ -25,7 +25,7 @@ import android.util.ArraySet; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag; import javax.inject.Inject; @@ -83,7 +83,7 @@ public class AmbientPulseManager extends AlertingNotificationManager { @Override protected void onAlertEntryAdded(AlertEntry alertEntry) { - NotificationData.Entry entry = alertEntry.mEntry; + NotificationEntry entry = alertEntry.mEntry; entry.setAmbientPulsing(true); for (OnAmbientChangedListener listener : mListeners) { listener.onAmbientStateChanged(entry, true); @@ -92,7 +92,7 @@ public class AmbientPulseManager extends AlertingNotificationManager { @Override protected void onAlertEntryRemoved(AlertEntry alertEntry) { - NotificationData.Entry entry = alertEntry.mEntry; + NotificationEntry entry = alertEntry.mEntry; entry.setAmbientPulsing(false); for (OnAmbientChangedListener listener : mListeners) { listener.onAmbientStateChanged(entry, false); @@ -131,7 +131,7 @@ public class AmbientPulseManager extends AlertingNotificationManager { * @param entry the entry that changed * @param isPulsing true if the entry is now pulsing, false otherwise */ - void onAmbientStateChanged(NotificationData.Entry entry, boolean isPulsing); + void onAmbientStateChanged(NotificationEntry entry, boolean isPulsing); } private final class AmbientEntry extends AlertEntry { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java index e2177774a75d..3f1ff33437b9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java @@ -32,7 +32,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.AlphaOptimizedLinearLayout; import com.android.systemui.R; import com.android.systemui.plugins.DarkIconDispatcher; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import java.util.List; @@ -50,7 +50,7 @@ public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout { private int mEndMargin; private View mIconPlaceholder; private TextView mTextView; - private NotificationData.Entry mShowingEntry; + private NotificationEntry mShowingEntry; private Rect mLayoutedIconRect = new Rect(); private int[] mTmpPosition = new int[2]; private boolean mFirstLayout = true; @@ -162,7 +162,7 @@ public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout { mTextView = findViewById(R.id.text); } - public void setEntry(NotificationData.Entry entry) { + public void setEntry(NotificationEntry entry) { if (entry != null) { mShowingEntry = entry; CharSequence text = entry.headsUpStatusBarText; @@ -261,7 +261,7 @@ public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout { return super.fitSystemWindows(insets); } - public NotificationData.Entry getShowingEntry() { + public NotificationEntry getShowingEntry() { return mShowingEntry; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java index ecd9814c3073..0f295ba75fe4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java @@ -2,7 +2,7 @@ package com.android.systemui.statusbar; import androidx.annotation.NonNull; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; /** * Interface for anything that may need to keep notifications managed even after @@ -24,7 +24,7 @@ public interface NotificationLifetimeExtender { * @param entry the entry containing the notification to check * @return true if the notification lifetime should be extended */ - boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry); + boolean shouldExtendLifetime(@NonNull NotificationEntry entry); /** * Sets whether or not the lifetime should be managed by the extender. In practice, if @@ -37,7 +37,7 @@ public interface NotificationLifetimeExtender { * @param entry the entry that needs an extended lifetime * @param shouldManage true if the extender should manage the entry now, false otherwise */ - void setShouldManageLifetime(@NonNull NotificationData.Entry entry, boolean shouldManage); + void setShouldManageLifetime(@NonNull NotificationEntry entry, boolean shouldManage); /** * The callback for when the notification is now safe to remove (i.e. its lifetime has ended). diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java index bc662e3d8855..f46ded4d61d8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java @@ -18,7 +18,7 @@ import android.content.pm.UserInfo; import android.service.notification.StatusBarNotification; import android.util.SparseArray; -import com.android.systemui.statusbar.notification.NotificationData.Entry; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; public interface NotificationLockscreenUserManager { String PERMISSION_SELF = "com.android.systemui.permission.SELF"; @@ -55,7 +55,7 @@ public interface NotificationLockscreenUserManager { void updatePublicMode(); - boolean needsRedaction(Entry entry); + boolean needsRedaction(NotificationEntry entry); boolean userAllowsPrivateNotificationsInPublic(int currentUserId); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index bba4369b5e01..d5f4d0461ba4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -46,9 +46,9 @@ import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.statusbar.StatusBarStateController.StateListener; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationUtils; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.KeyguardMonitor; @@ -407,7 +407,7 @@ public class NotificationLockscreenUserManagerImpl implements } /** @return true if the entry needs redaction when on the lockscreen. */ - public boolean needsRedaction(NotificationData.Entry ent) { + public boolean needsRedaction(NotificationEntry ent) { int userId = ent.notification.getUserId(); boolean currentUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(mCurrentUserId); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index e59bc2a82c4c..7412702abfea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -46,9 +46,9 @@ import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.Interpolators; import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.statusbar.notification.NotificationData.Entry; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.LockscreenWallpaper; import com.android.systemui.statusbar.phone.ScrimController; @@ -156,7 +156,7 @@ public class NotificationMediaManager implements Dumpable { notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() { @Override public void onEntryRemoved( - Entry entry, + NotificationEntry entry, NotificationVisibility visibility, boolean removedByUser) { onNotificationRemoved(entry.key); @@ -188,7 +188,7 @@ public class NotificationMediaManager implements Dumpable { return null; } synchronized (mEntryManager.getNotificationData()) { - Entry entry = mEntryManager.getNotificationData().get(mMediaNotificationKey); + NotificationEntry entry = mEntryManager.getNotificationData().get(mMediaNotificationKey); if (entry == null || entry.expandedIcon == null) { return null; } @@ -210,15 +210,15 @@ public class NotificationMediaManager implements Dumpable { boolean metaDataChanged = false; synchronized (mEntryManager.getNotificationData()) { - ArrayList<Entry> activeNotifications = + ArrayList<NotificationEntry> activeNotifications = mEntryManager.getNotificationData().getActiveNotifications(); final int N = activeNotifications.size(); // Promote the media notification with a controller in 'playing' state, if any. - Entry mediaNotification = null; + NotificationEntry mediaNotification = null; MediaController controller = null; for (int i = 0; i < N; i++) { - final Entry entry = activeNotifications.get(i); + final NotificationEntry entry = activeNotifications.get(i); if (entry.isMediaNotification()) { final MediaSession.Token token = @@ -258,7 +258,7 @@ public class NotificationMediaManager implements Dumpable { final String pkg = aController.getPackageName(); for (int i = 0; i < N; i++) { - final Entry entry = activeNotifications.get(i); + final NotificationEntry entry = activeNotifications.get(i); if (entry.notification.getPackageName().equals(pkg)) { if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: found controller matching " diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index 1ab9c5c2b4a2..7d6231f27885 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -51,9 +51,9 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dumpable; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.RemoteInputView; @@ -103,7 +103,7 @@ public class NotificationRemoteInputManager implements Dumpable { * Notifications that are already removed but are kept around because the remote input is * actively being used (i.e. user is typing in it). See {@link RemoteInputActiveExtender}. */ - protected final ArraySet<NotificationData.Entry> mEntriesKeptForRemoteInputActive = + protected final ArraySet<NotificationEntry> mEntriesKeptForRemoteInputActive = new ArraySet<>(); // Dependencies: @@ -253,7 +253,7 @@ public class NotificationRemoteInputManager implements Dumpable { notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() { @Override public void onEntryRemoved( - @Nullable NotificationData.Entry entry, + @Nullable NotificationEntry entry, NotificationVisibility visibility, boolean removedByUser) { if (removedByUser && entry != null) { @@ -269,7 +269,7 @@ public class NotificationRemoteInputManager implements Dumpable { mRemoteInputController = new RemoteInputController(delegate); mRemoteInputController.addCallback(new RemoteInputController.Callback() { @Override - public void onRemoteInputSent(NotificationData.Entry entry) { + public void onRemoteInputSent(NotificationEntry entry) { if (FORCE_REMOTE_INPUT_HISTORY && isNotificationKeptForRemoteInputHistory(entry.key)) { mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key); @@ -413,7 +413,7 @@ public class NotificationRemoteInputManager implements Dumpable { } @VisibleForTesting - void onPerformRemoveNotification(NotificationData.Entry entry, final String key) { + void onPerformRemoveNotification(NotificationEntry entry, final String key) { if (mKeysKeptForRemoteInputHistory.contains(key)) { mKeysKeptForRemoteInputHistory.remove(key); } @@ -424,7 +424,7 @@ public class NotificationRemoteInputManager implements Dumpable { public void onPanelCollapsed() { for (int i = 0; i < mEntriesKeptForRemoteInputActive.size(); i++) { - NotificationData.Entry entry = mEntriesKeptForRemoteInputActive.valueAt(i); + NotificationEntry entry = mEntriesKeptForRemoteInputActive.valueAt(i); mRemoteInputController.removeRemoteInput(entry, null); if (mNotificationLifetimeFinishedCallback != null) { mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key); @@ -437,7 +437,7 @@ public class NotificationRemoteInputManager implements Dumpable { return mKeysKeptForRemoteInputHistory.contains(key); } - public boolean shouldKeepForRemoteInputHistory(NotificationData.Entry entry) { + public boolean shouldKeepForRemoteInputHistory(NotificationEntry entry) { if (entry.isDismissed()) { return false; } @@ -447,7 +447,7 @@ public class NotificationRemoteInputManager implements Dumpable { return (mRemoteInputController.isSpinning(entry.key) || entry.hasJustSentRemoteInput()); } - public boolean shouldKeepForSmartReplyHistory(NotificationData.Entry entry) { + public boolean shouldKeepForSmartReplyHistory(NotificationEntry entry) { if (entry.isDismissed()) { return false; } @@ -467,13 +467,13 @@ public class NotificationRemoteInputManager implements Dumpable { @VisibleForTesting StatusBarNotification rebuildNotificationForCanceledSmartReplies( - NotificationData.Entry entry) { + NotificationEntry entry) { return rebuildNotificationWithRemoteInput(entry, null /* remoteInputTest */, false /* showSpinner */); } @VisibleForTesting - StatusBarNotification rebuildNotificationWithRemoteInput(NotificationData.Entry entry, + StatusBarNotification rebuildNotificationWithRemoteInput(NotificationEntry entry, CharSequence remoteInputText, boolean showSpinner) { StatusBarNotification sbn = entry.notification; @@ -530,7 +530,7 @@ public class NotificationRemoteInputManager implements Dumpable { } @VisibleForTesting - public Set<NotificationData.Entry> getEntriesKeptForRemoteInputActive() { + public Set<NotificationEntry> getEntriesKeptForRemoteInputActive() { return mEntriesKeptForRemoteInputActive; } @@ -553,12 +553,12 @@ public class NotificationRemoteInputManager implements Dumpable { */ protected class RemoteInputHistoryExtender extends RemoteInputExtender { @Override - public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) { + public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) { return shouldKeepForRemoteInputHistory(entry); } @Override - public void setShouldManageLifetime(NotificationData.Entry entry, + public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) { if (shouldExtend) { CharSequence remoteInputText = entry.remoteInputText; @@ -599,12 +599,12 @@ public class NotificationRemoteInputManager implements Dumpable { */ protected class SmartReplyHistoryExtender extends RemoteInputExtender { @Override - public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) { + public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) { return shouldKeepForSmartReplyHistory(entry); } @Override - public void setShouldManageLifetime(NotificationData.Entry entry, + public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) { if (shouldExtend) { StatusBarNotification newSbn = rebuildNotificationForCanceledSmartReplies(entry); @@ -637,7 +637,7 @@ public class NotificationRemoteInputManager implements Dumpable { */ protected class RemoteInputActiveExtender extends RemoteInputExtender { @Override - public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) { + public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) { if (entry.isDismissed()) { return false; } @@ -645,7 +645,7 @@ public class NotificationRemoteInputManager implements Dumpable { } @Override - public void setShouldManageLifetime(NotificationData.Entry entry, + public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) { if (shouldExtend) { if (Log.isLoggable(TAG, Log.DEBUG)) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java index f23ae3f6bfb7..f0d804dbc7a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java @@ -24,7 +24,7 @@ import android.text.TextUtils; import androidx.annotation.VisibleForTesting; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import java.util.ArrayList; import java.util.Arrays; @@ -52,7 +52,7 @@ public class NotificationUiAdjustment { } public static NotificationUiAdjustment extractFromNotificationEntry( - NotificationData.Entry entry) { + NotificationEntry entry) { return new NotificationUiAdjustment( entry.key, entry.systemGeneratedSmartActions, entry.smartReplies); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index 017a9c368fdf..bf6caa010e9b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -27,9 +27,9 @@ import android.view.ViewGroup; import com.android.systemui.R; import com.android.systemui.bubbles.BubbleController; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.NotificationGroupManager; @@ -150,13 +150,13 @@ public class NotificationViewHierarchyManager { */ //TODO: Rewrite this to focus on Entries, or some other data object instead of views public void updateNotificationViews() { - ArrayList<NotificationData.Entry> activeNotifications = mEntryManager.getNotificationData() + ArrayList<NotificationEntry> activeNotifications = mEntryManager.getNotificationData() .getActiveNotifications(); ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size()); - ArrayList<NotificationData.Entry> toBubble = new ArrayList<>(); + ArrayList<NotificationEntry> toBubble = new ArrayList<>(); final int N = activeNotifications.size(); for (int i = 0; i < N; i++) { - NotificationData.Entry ent = activeNotifications.get(i); + NotificationEntry ent = activeNotifications.get(i); if (ent.isRowDismissed() || ent.isRowRemoved()) { // we don't want to update removed notifications because they could // temporarily become children if they were isolated before. @@ -187,7 +187,7 @@ public class NotificationViewHierarchyManager { ent.getRow().setSensitive(sensitive, deviceSensitive); ent.getRow().setNeedsRedaction(needsRedaction); if (mGroupManager.isChildInGroupWithSummary(ent.notification)) { - NotificationData.Entry summary = mGroupManager.getGroupSummary(ent.notification); + NotificationEntry summary = mGroupManager.getGroupSummary(ent.notification); List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(summary.getRow()); if (orderedChildren == null) { @@ -271,7 +271,7 @@ public class NotificationViewHierarchyManager { for (int i = 0; i < toBubble.size(); i++) { // TODO: might make sense to leave them in the shade and just reposition them - NotificationData.Entry ent = toBubble.get(i); + NotificationEntry ent = toBubble.get(i); mBubbleController.addBubble(ent); } @@ -385,7 +385,7 @@ public class NotificationViewHierarchyManager { } while(!stack.isEmpty()) { ExpandableNotificationRow row = stack.pop(); - NotificationData.Entry entry = row.getEntry(); + NotificationEntry entry = row.getEntry(); boolean isChildNotification = mGroupManager.isChildInGroupWithSummary(entry.notification); @@ -408,7 +408,7 @@ public class NotificationViewHierarchyManager { if (!showOnKeyguard) { // min priority notifications should show if their summary is showing if (mGroupManager.isChildInGroupWithSummary(entry.notification)) { - NotificationData.Entry summary = mGroupManager.getLogicalGroupSummary( + NotificationEntry summary = mGroupManager.getLogicalGroupSummary( entry.notification); if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard( summary.notification)) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java index e8abcc2bd80e..998cf523d3df 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java @@ -24,7 +24,7 @@ import android.util.ArrayMap; import android.util.Pair; import com.android.internal.util.Preconditions; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.policy.RemoteInputView; import java.lang.ref.WeakReference; @@ -38,7 +38,7 @@ public class RemoteInputController { private static final boolean ENABLE_REMOTE_INPUT = SystemProperties.getBoolean("debug.enable_remote_input", true); - private final ArrayList<Pair<WeakReference<NotificationData.Entry>, Object>> mOpen + private final ArrayList<Pair<WeakReference<NotificationEntry>, Object>> mOpen = new ArrayList<>(); private final ArrayMap<String, Object> mSpinning = new ArrayMap<>(); private final ArrayList<Callback> mCallbacks = new ArrayList<>(3); @@ -101,7 +101,7 @@ public class RemoteInputController { * @param entry the entry for which a remote input is now active. * @param token a token identifying the view that is managing the remote input */ - public void addRemoteInput(NotificationData.Entry entry, Object token) { + public void addRemoteInput(NotificationEntry entry, Object token) { Preconditions.checkNotNull(entry); Preconditions.checkNotNull(token); @@ -122,7 +122,7 @@ public class RemoteInputController { * the entry is only removed if the token matches the last added token for this * entry. If null, the entry is removed regardless. */ - public void removeRemoteInput(NotificationData.Entry entry, Object token) { + public void removeRemoteInput(NotificationEntry entry, Object token) { Preconditions.checkNotNull(entry); pruneWeakThenRemoveAndContains(null /* contains */, entry /* remove */, token); @@ -173,7 +173,7 @@ public class RemoteInputController { return mSpinning.get(key) == token; } - private void apply(NotificationData.Entry entry) { + private void apply(NotificationEntry entry) { mDelegate.setRemoteInputActive(entry, isRemoteInputActive(entry)); boolean remoteInputActive = isRemoteInputActive(); int N = mCallbacks.size(); @@ -185,7 +185,7 @@ public class RemoteInputController { /** * @return true if {@param entry} has an active RemoteInput */ - public boolean isRemoteInputActive(NotificationData.Entry entry) { + public boolean isRemoteInputActive(NotificationEntry entry) { return pruneWeakThenRemoveAndContains(entry /* contains */, null /* remove */, null /* removeToken */); } @@ -208,10 +208,10 @@ public class RemoteInputController { * @return true if {@param contains} is in the set of active remote inputs */ private boolean pruneWeakThenRemoveAndContains( - NotificationData.Entry contains, NotificationData.Entry remove, Object removeToken) { + NotificationEntry contains, NotificationEntry remove, Object removeToken) { boolean found = false; for (int i = mOpen.size() - 1; i >= 0; i--) { - NotificationData.Entry item = mOpen.get(i).first.get(); + NotificationEntry item = mOpen.get(i).first.get(); Object itemToken = mOpen.get(i).second; boolean removeTokenMatches = (removeToken == null || itemToken == removeToken); @@ -235,7 +235,7 @@ public class RemoteInputController { mCallbacks.add(callback); } - public void remoteInputSent(NotificationData.Entry entry) { + public void remoteInputSent(NotificationEntry entry) { int N = mCallbacks.size(); for (int i = 0; i < N; i++) { mCallbacks.get(i).onRemoteInputSent(entry); @@ -248,16 +248,16 @@ public class RemoteInputController { } // Make a copy because closing the remote inputs will modify mOpen. - ArrayList<NotificationData.Entry> list = new ArrayList<>(mOpen.size()); + ArrayList<NotificationEntry> list = new ArrayList<>(mOpen.size()); for (int i = mOpen.size() - 1; i >= 0; i--) { - NotificationData.Entry entry = mOpen.get(i).first.get(); + NotificationEntry entry = mOpen.get(i).first.get(); if (entry != null && entry.rowExists()) { list.add(entry); } } for (int i = list.size() - 1; i >= 0; i--) { - NotificationData.Entry entry = list.get(i); + NotificationEntry entry = list.get(i); if (entry.rowExists()) { entry.closeRemoteInput(); } @@ -268,31 +268,31 @@ public class RemoteInputController { mDelegate.requestDisallowLongPressAndDismiss(); } - public void lockScrollTo(NotificationData.Entry entry) { + public void lockScrollTo(NotificationEntry entry) { mDelegate.lockScrollTo(entry); } public interface Callback { default void onRemoteInputActive(boolean active) {} - default void onRemoteInputSent(NotificationData.Entry entry) {} + default void onRemoteInputSent(NotificationEntry entry) {} } public interface Delegate { /** * Activate remote input if necessary. */ - void setRemoteInputActive(NotificationData.Entry entry, boolean remoteInputActive); - - /** - * Request that the view does not dismiss nor perform long press for the current touch. - */ - void requestDisallowLongPressAndDismiss(); - - /** - * Request that the view is made visible by scrolling to it, and keep the scroll locked until - * the user scrolls, or {@param v} loses focus or is detached. - */ - void lockScrollTo(NotificationData.Entry entry); + void setRemoteInputActive(NotificationEntry entry, boolean remoteInputActive); + + /** + * Request that the view does not dismiss nor perform long press for the current touch. + */ + void requestDisallowLongPressAndDismiss(); + + /** + * Request that the view is made visible by scrolling to it, and keep the scroll locked until + * the user scrolls, or {@param entry} loses focus or is detached. + */ + void lockScrollTo(NotificationEntry entry); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java index 9e91133aff09..573c1f8ee509 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java @@ -21,8 +21,8 @@ import android.util.ArraySet; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import java.util.Set; @@ -54,7 +54,7 @@ public class SmartReplyController { /** * Notifies StatusBarService a smart reply is sent. */ - public void smartReplySent(NotificationData.Entry entry, int replyIndex, CharSequence reply, + public void smartReplySent(NotificationEntry entry, int replyIndex, CharSequence reply, boolean generatedByAssistant) { mCallback.onSmartReplySent(entry, reply); mSendingKeys.add(entry.key); @@ -70,7 +70,7 @@ public class SmartReplyController { * Notifies StatusBarService a smart action is clicked. */ public void smartActionClicked( - NotificationData.Entry entry, int actionIndex, Notification.Action action, + NotificationEntry entry, int actionIndex, Notification.Action action, boolean generatedByAssistant) { final int count = mEntryManager.getNotificationData().getActiveNotifications().size(); final int rank = mEntryManager.getNotificationData().getRank(entry.key); @@ -95,7 +95,7 @@ public class SmartReplyController { /** * Smart Replies and Actions have been added to the UI. */ - public void smartSuggestionsAdded(final NotificationData.Entry entry, int replyCount, + public void smartSuggestionsAdded(final NotificationEntry entry, int replyCount, int actionCount, boolean generatedByAssistant) { try { mBarService.onNotificationSmartSuggestionsAdded( @@ -105,7 +105,7 @@ public class SmartReplyController { } } - public void stopSending(final NotificationData.Entry entry) { + public void stopSending(final NotificationEntry entry) { if (entry != null) { mSendingKeys.remove(entry.notification.getKey()); } @@ -121,6 +121,6 @@ public class SmartReplyController { * @param entry the entry for the notification * @param reply the reply that was sent */ - void onSmartReplySent(NotificationData.Entry entry, CharSequence reply); + void onSmartReplySent(NotificationEntry entry, CharSequence reply); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java index 2bb0d5ce9161..c24698deabde 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java @@ -29,6 +29,7 @@ import com.android.systemui.statusbar.AlertingNotificationManager; import com.android.systemui.statusbar.AmbientPulseManager; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationInflater; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -71,18 +72,18 @@ public class NotificationAlertingManager { notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() { @Override - public void onEntryInflated(NotificationData.Entry entry, int inflatedFlags) { + public void onEntryInflated(NotificationEntry entry, int inflatedFlags) { showAlertingView(entry, inflatedFlags); } @Override - public void onEntryUpdated(NotificationData.Entry entry) { + public void onEntryUpdated(NotificationEntry entry) { updateAlertState(entry); } @Override public void onEntryRemoved( - NotificationData.Entry entry, + NotificationEntry entry, NotificationVisibility visibility, boolean removedByUser) { stopAlerting(entry.key); @@ -101,7 +102,7 @@ public class NotificationAlertingManager { * @param entry entry to add * @param inflatedFlags flags representing content views that were inflated */ - private void showAlertingView(NotificationData.Entry entry, + private void showAlertingView(NotificationEntry entry, @NotificationInflater.InflationFlag int inflatedFlags) { if ((inflatedFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) { // Possible for shouldHeadsUp to change between the inflation starting and ending. @@ -123,7 +124,7 @@ public class NotificationAlertingManager { } } - private void updateAlertState(NotificationData.Entry entry) { + private void updateAlertState(NotificationEntry entry) { boolean alertAgain = alertAgain(entry, entry.notification.getNotification()); AlertingNotificationManager alertManager; boolean shouldAlert; @@ -150,7 +151,7 @@ public class NotificationAlertingManager { } private static boolean alertAgain( - NotificationData.Entry oldEntry, Notification newNotification) { + NotificationEntry oldEntry, Notification newNotification) { return oldEntry == null || !oldEntry.hasInterrupted() || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java deleted file mode 100644 index a51896ee69fc..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java +++ /dev/null @@ -1,1072 +0,0 @@ -/* - * Copyright (C) 2008 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.systemui.statusbar.notification; - -import static android.app.Notification.CATEGORY_ALARM; -import static android.app.Notification.CATEGORY_CALL; -import static android.app.Notification.CATEGORY_EVENT; -import static android.app.Notification.CATEGORY_MESSAGE; -import static android.app.Notification.CATEGORY_REMINDER; -import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT; -import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; -import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST; -import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; -import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; - -import android.annotation.NonNull; -import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.app.Person; -import android.content.Context; -import android.graphics.drawable.Icon; -import android.os.Bundle; -import android.os.Parcelable; -import android.os.SystemClock; -import android.service.notification.NotificationListenerService.Ranking; -import android.service.notification.NotificationListenerService.RankingMap; -import android.service.notification.SnoozeCriterion; -import android.service.notification.StatusBarNotification; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.view.View; -import android.widget.ImageView; - -import androidx.annotation.Nullable; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.statusbar.StatusBarIcon; -import com.android.internal.util.ArrayUtils; -import com.android.internal.util.ContrastColorUtil; -import com.android.systemui.Dependency; -import com.android.systemui.statusbar.InflationTask; -import com.android.systemui.statusbar.NotificationMediaManager; -import com.android.systemui.statusbar.StatusBarIconView; -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.notification.row.NotificationGuts; -import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag; -import com.android.systemui.statusbar.phone.NotificationGroupManager; -import com.android.systemui.statusbar.policy.HeadsUpManager; - -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Objects; - -/** - * The list of currently displaying notifications. - */ -public class NotificationData { - - private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class); - - /** - * These dependencies are late init-ed - */ - private KeyguardEnvironment mEnvironment; - private NotificationMediaManager mMediaManager; - - private HeadsUpManager mHeadsUpManager; - - public static final class Entry { - private static final long LAUNCH_COOLDOWN = 2000; - private static final long REMOTE_INPUT_COOLDOWN = 500; - private static final long INITIALIZATION_DELAY = 400; - private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN; - private static final int COLOR_INVALID = 1; - public final String key; - public StatusBarNotification notification; - public NotificationChannel channel; - public long lastAudiblyAlertedMs; - public boolean noisy; - public boolean ambient; - public int importance; - public StatusBarIconView icon; - public StatusBarIconView expandedIcon; - private boolean interruption; - public boolean autoRedacted; // whether the redacted notification was generated by us - public int targetSdk; - private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET; - public CharSequence remoteInputText; - public List<SnoozeCriterion> snoozeCriteria; - public int userSentiment = Ranking.USER_SENTIMENT_NEUTRAL; - /** Smart Actions provided by the NotificationAssistantService. */ - @NonNull - public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList(); - public CharSequence[] smartReplies = new CharSequence[0]; - @VisibleForTesting - public int suppressedVisualEffects; - public boolean suspended; - - private Entry parent; // our parent (if we're in a group) - private ArrayList<Entry> children = new ArrayList<Entry>(); - private ExpandableNotificationRow row; // the outer expanded view - - private int mCachedContrastColor = COLOR_INVALID; - private int mCachedContrastColorIsFor = COLOR_INVALID; - private InflationTask mRunningTask = null; - private Throwable mDebugThrowable; - public CharSequence remoteInputTextWhenReset; - public long lastRemoteInputSent = NOT_LAUNCHED_YET; - public ArraySet<Integer> mActiveAppOps = new ArraySet<>(3); - public CharSequence headsUpStatusBarText; - public CharSequence headsUpStatusBarTextPublic; - - private long initializationTime = -1; - - /** - * Whether or not this row represents a system notification. Note that if this is - * {@code null}, that means we were either unable to retrieve the info or have yet to - * retrieve the info. - */ - public Boolean mIsSystemNotification; - - /** - * Has the user sent a reply through this Notification. - */ - private boolean hasSentReply; - - /** - * Whether this notification should be displayed as a bubble. - */ - private boolean mIsBubble; - - /** - * Whether the user has dismissed this notification when it was in bubble form. - */ - private boolean mUserDismissedBubble; - - public Entry(StatusBarNotification n) { - this(n, null); - } - - public Entry(StatusBarNotification n, @Nullable Ranking ranking) { - this.key = n.getKey(); - this.notification = n; - if (ranking != null) { - populateFromRanking(ranking); - } - } - - public void populateFromRanking(@NonNull Ranking ranking) { - channel = ranking.getChannel(); - lastAudiblyAlertedMs = ranking.getLastAudiblyAlertedMillis(); - importance = ranking.getImportance(); - ambient = ranking.isAmbient(); - snoozeCriteria = ranking.getSnoozeCriteria(); - userSentiment = ranking.getUserSentiment(); - systemGeneratedSmartActions = ranking.getSmartActions() == null - ? Collections.emptyList() : ranking.getSmartActions(); - smartReplies = ranking.getSmartReplies() == null - ? new CharSequence[0] - : ranking.getSmartReplies().toArray(new CharSequence[0]); - suppressedVisualEffects = ranking.getSuppressedVisualEffects(); - suspended = ranking.isSuspended(); - } - - public void setInterruption() { - interruption = true; - } - - public boolean hasInterrupted() { - return interruption; - } - - public void setIsBubble(boolean bubbleable) { - mIsBubble = bubbleable; - } - - public boolean isBubble() { - return mIsBubble; - } - - public void setBubbleDismissed(boolean userDismissed) { - mUserDismissedBubble = userDismissed; - } - - public boolean isBubbleDismissed() { - return mUserDismissedBubble; - } - - /** - * Resets the notification entry to be re-used. - */ - public void reset() { - if (row != null) { - row.reset(); - } - } - - public ExpandableNotificationRow getRow() { - return row; - } - - //TODO: This will go away when we have a way to bind an entry to a row - public void setRow(ExpandableNotificationRow row) { - this.row = row; - } - - @Nullable - public List<Entry> getChildren() { - if (children.size() <= 0) { - return null; - } - - return children; - } - - public void notifyFullScreenIntentLaunched() { - setInterruption(); - lastFullScreenIntentLaunchTime = SystemClock.elapsedRealtime(); - } - - public boolean hasJustLaunchedFullScreenIntent() { - return SystemClock.elapsedRealtime() < lastFullScreenIntentLaunchTime + LAUNCH_COOLDOWN; - } - - public boolean hasJustSentRemoteInput() { - return SystemClock.elapsedRealtime() < lastRemoteInputSent + REMOTE_INPUT_COOLDOWN; - } - - public boolean hasFinishedInitialization() { - return initializationTime == -1 || - SystemClock.elapsedRealtime() > initializationTime + INITIALIZATION_DELAY; - } - - /** - * Create the icons for a notification - * @param context the context to create the icons with - * @param sbn the notification - * @throws InflationException - */ - public void createIcons(Context context, StatusBarNotification sbn) - throws InflationException { - Notification n = sbn.getNotification(); - final Icon smallIcon = n.getSmallIcon(); - if (smallIcon == null) { - throw new InflationException("No small icon in notification from " - + sbn.getPackageName()); - } - - // Construct the icon. - icon = new StatusBarIconView(context, - sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn); - icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE); - - // Construct the expanded icon. - expandedIcon = new StatusBarIconView(context, - sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn); - expandedIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE); - final StatusBarIcon ic = new StatusBarIcon( - sbn.getUser(), - sbn.getPackageName(), - smallIcon, - n.iconLevel, - n.number, - StatusBarIconView.contentDescForNotification(context, n)); - if (!icon.set(ic) || !expandedIcon.set(ic)) { - icon = null; - expandedIcon = null; - throw new InflationException("Couldn't create icon: " + ic); - } - expandedIcon.setVisibility(View.INVISIBLE); - expandedIcon.setOnVisibilityChangedListener( - newVisibility -> { - if (row != null) { - row.setIconsVisible(newVisibility != View.VISIBLE); - } - }); - } - - public void setIconTag(int key, Object tag) { - if (icon != null) { - icon.setTag(key, tag); - expandedIcon.setTag(key, tag); - } - } - - /** - * Update the notification icons. - * - * @param context the context to create the icons with. - * @param sbn the notification to read the icon from. - * @throws InflationException - */ - public void updateIcons(Context context, StatusBarNotification sbn) - throws InflationException { - if (icon != null) { - // Update the icon - Notification n = sbn.getNotification(); - final StatusBarIcon ic = new StatusBarIcon( - notification.getUser(), - notification.getPackageName(), - n.getSmallIcon(), - n.iconLevel, - n.number, - StatusBarIconView.contentDescForNotification(context, n)); - icon.setNotification(sbn); - expandedIcon.setNotification(sbn); - if (!icon.set(ic) || !expandedIcon.set(ic)) { - throw new InflationException("Couldn't update icon: " + ic); - } - } - } - - public int getContrastedColor(Context context, boolean isLowPriority, - int backgroundColor) { - int rawColor = isLowPriority ? Notification.COLOR_DEFAULT : - notification.getNotification().color; - if (mCachedContrastColorIsFor == rawColor && mCachedContrastColor != COLOR_INVALID) { - return mCachedContrastColor; - } - final int contrasted = ContrastColorUtil.resolveContrastColor(context, rawColor, - backgroundColor); - mCachedContrastColorIsFor = rawColor; - mCachedContrastColor = contrasted; - return mCachedContrastColor; - } - - /** - * Abort all existing inflation tasks - */ - public void abortTask() { - if (mRunningTask != null) { - mRunningTask.abort(); - mRunningTask = null; - } - } - - public void setInflationTask(InflationTask abortableTask) { - // abort any existing inflation - InflationTask existing = mRunningTask; - abortTask(); - mRunningTask = abortableTask; - if (existing != null && mRunningTask != null) { - mRunningTask.supersedeTask(existing); - } - } - - public void onInflationTaskFinished() { - mRunningTask = null; - } - - @VisibleForTesting - public InflationTask getRunningTask() { - return mRunningTask; - } - - /** - * Set a throwable that is used for debugging - * - * @param debugThrowable the throwable to save - */ - public void setDebugThrowable(Throwable debugThrowable) { - mDebugThrowable = debugThrowable; - } - - public Throwable getDebugThrowable() { - return mDebugThrowable; - } - - public void onRemoteInputInserted() { - lastRemoteInputSent = NOT_LAUNCHED_YET; - remoteInputTextWhenReset = null; - } - - public void setHasSentReply() { - hasSentReply = true; - } - - public boolean isLastMessageFromReply() { - if (!hasSentReply) { - return false; - } - Bundle extras = notification.getNotification().extras; - CharSequence[] replyTexts = extras.getCharSequenceArray( - Notification.EXTRA_REMOTE_INPUT_HISTORY); - if (!ArrayUtils.isEmpty(replyTexts)) { - return true; - } - Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES); - if (messages != null && messages.length > 0) { - Parcelable message = messages[messages.length - 1]; - if (message instanceof Bundle) { - Notification.MessagingStyle.Message lastMessage = - Notification.MessagingStyle.Message.getMessageFromBundle( - (Bundle) message); - if (lastMessage != null) { - Person senderPerson = lastMessage.getSenderPerson(); - if (senderPerson == null) { - return true; - } - Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON); - return Objects.equals(user, senderPerson); - } - } - } - return false; - } - - public void setInitializationTime(long time) { - if (initializationTime == -1) { - initializationTime = time; - } - } - - public void sendAccessibilityEvent(int eventType) { - if (row != null) { - row.sendAccessibilityEvent(eventType); - } - } - - /** - * Used by NotificationMediaManager to determine... things - * @return {@code true} if we are a media notification - */ - public boolean isMediaNotification() { - if (row == null) return false; - - return row.isMediaRow(); - } - - /** - * We are a top level child if our parent is the list of notifications duh - * @return {@code true} if we're a top level notification - */ - public boolean isTopLevelChild() { - return row != null && row.isTopLevelChild(); - } - - public void resetUserExpansion() { - if (row != null) row.resetUserExpansion(); - } - - public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) { - if (row != null) row.freeContentViewWhenSafe(inflationFlag); - } - - public void setAmbientPulsing(boolean pulsing) { - if (row != null) row.setAmbientPulsing(pulsing); - } - - public boolean rowExists() { - return row != null; - } - - public boolean isRowDismissed() { - return row != null && row.isDismissed(); - } - - public boolean isRowRemoved() { - return row != null && row.isRemoved(); - } - - /** - * @return {@code true} if the row is null or removed - */ - public boolean isRemoved() { - //TODO: recycling invalidates this - return row == null || row.isRemoved(); - } - - /** - * @return {@code true} if the row is null or dismissed - */ - public boolean isDismissed() { - //TODO: recycling - return row == null || row.isDismissed(); - } - - public boolean isRowPinned() { - return row != null && row.isPinned(); - } - - public void setRowPinned(boolean pinned) { - if (row != null) row.setPinned(pinned); - } - - public boolean isRowAnimatingAway() { - return row != null && row.isHeadsUpAnimatingAway(); - } - - public boolean isRowHeadsUp() { - return row != null && row.isHeadsUp(); - } - - public void setHeadsUp(boolean shouldHeadsUp) { - if (row != null) row.setHeadsUp(shouldHeadsUp); - } - - public boolean mustStayOnScreen() { - return row != null && row.mustStayOnScreen(); - } - - public void setHeadsUpIsVisible() { - if (row != null) row.setHeadsUpIsVisible(); - } - - //TODO: i'm imagining a world where this isn't just the row, but I could be rwong - public ExpandableNotificationRow getHeadsUpAnimationView() { - return row; - } - - public void setUserLocked(boolean userLocked) { - if (row != null) row.setUserLocked(userLocked); - } - - public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) { - if (row != null) row.setUserExpanded(userExpanded, allowChildExpansion); - } - - public void setGroupExpansionChanging(boolean changing) { - if (row != null) row.setGroupExpansionChanging(changing); - } - - public void notifyHeightChanged(boolean needsAnimation) { - if (row != null) row.notifyHeightChanged(needsAnimation); - } - - public void closeRemoteInput() { - if (row != null) row.closeRemoteInput(); - } - - public boolean areChildrenExpanded() { - return row != null && row.areChildrenExpanded(); - } - - public boolean keepInParent() { - return row != null && row.keepInParent(); - } - - //TODO: probably less confusing to say "is group fully visible" - public boolean isGroupNotFullyVisible() { - return row == null || row.isGroupNotFullyVisible(); - } - - public NotificationGuts getGuts() { - if (row != null) return row.getGuts(); - return null; - } - - public boolean hasLowPriorityStateUpdated() { - return row != null && row.hasLowPriorityStateUpdated(); - } - - public void removeRow() { - if (row != null) row.setRemoved(); - } - - public boolean isSummaryWithChildren() { - return row != null && row.isSummaryWithChildren(); - } - - public void setKeepInParent(boolean keep) { - if (row != null) row.setKeepInParent(keep); - } - - public void onDensityOrFontScaleChanged() { - if (row != null) row.onDensityOrFontScaleChanged(); - } - - public boolean areGutsExposed() { - return row != null && row.getGuts() != null && row.getGuts().isExposed(); - } - - public boolean isChildInGroup() { - return parent == null; - } - - public void setLowPriorityStateUpdated(boolean updated) { - if (row != null) row.setLowPriorityStateUpdated(updated); - } - - /** - * @return Can the underlying notification be cleared? This can be different from whether the - * notification can be dismissed in case notifications are sensitive on the lockscreen. - * @see #canViewBeDismissed() - */ - public boolean isClearable() { - if (notification == null || !notification.isClearable()) { - return false; - } - if (children.size() > 0) { - for (int i = 0; i < children.size(); i++) { - Entry child = children.get(i); - if (!child.isClearable()) { - return false; - } - } - } - return true; - } - - public boolean canViewBeDismissed() { - if (row == null) return true; - return row.canViewBeDismissed(); - } - - boolean isExemptFromDndVisualSuppression() { - if (isNotificationBlockedByPolicy(notification.getNotification())) { - return false; - } - - if ((notification.getNotification().flags - & Notification.FLAG_FOREGROUND_SERVICE) != 0) { - return true; - } - if (notification.getNotification().isMediaNotification()) { - return true; - } - if (mIsSystemNotification != null && mIsSystemNotification) { - return true; - } - return false; - } - - private boolean shouldSuppressVisualEffect(int effect) { - if (isExemptFromDndVisualSuppression()) { - return false; - } - return (suppressedVisualEffects & effect) != 0; - } - - /** - * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_FULL_SCREEN_INTENT} - * is set for this entry. - */ - public boolean shouldSuppressFullScreenIntent() { - return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT); - } - - /** - * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_PEEK} - * is set for this entry. - */ - public boolean shouldSuppressPeek() { - return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_PEEK); - } - - /** - * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_STATUS_BAR} - * is set for this entry. - */ - public boolean shouldSuppressStatusBar() { - return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_STATUS_BAR); - } - - /** - * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_AMBIENT} - * is set for this entry. - */ - public boolean shouldSuppressAmbient() { - return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_AMBIENT); - } - - /** - * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_NOTIFICATION_LIST} - * is set for this entry. - */ - public boolean shouldSuppressNotificationList() { - return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_NOTIFICATION_LIST); - } - } - - private final ArrayMap<String, Entry> mEntries = new ArrayMap<>(); - private final ArrayList<Entry> mSortedAndFiltered = new ArrayList<>(); - private final ArrayList<Entry> mFilteredForUser = new ArrayList<>(); - - private final NotificationGroupManager mGroupManager - = Dependency.get(NotificationGroupManager.class); - - private RankingMap mRankingMap; - private final Ranking mTmpRanking = new Ranking(); - - public void setHeadsUpManager(HeadsUpManager headsUpManager) { - mHeadsUpManager = headsUpManager; - } - - private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() { - private final Ranking mRankingA = new Ranking(); - private final Ranking mRankingB = new Ranking(); - - @Override - public int compare(Entry a, Entry b) { - final StatusBarNotification na = a.notification; - final StatusBarNotification nb = b.notification; - int aImportance = NotificationManager.IMPORTANCE_DEFAULT; - int bImportance = NotificationManager.IMPORTANCE_DEFAULT; - int aRank = 0; - int bRank = 0; - - if (mRankingMap != null) { - // RankingMap as received from NoMan - getRanking(a.key, mRankingA); - getRanking(b.key, mRankingB); - aImportance = mRankingA.getImportance(); - bImportance = mRankingB.getImportance(); - aRank = mRankingA.getRank(); - bRank = mRankingB.getRank(); - } - - String mediaNotification = getMediaManager().getMediaNotificationKey(); - - // IMPORTANCE_MIN media streams are allowed to drift to the bottom - final boolean aMedia = a.key.equals(mediaNotification) - && aImportance > NotificationManager.IMPORTANCE_MIN; - final boolean bMedia = b.key.equals(mediaNotification) - && bImportance > NotificationManager.IMPORTANCE_MIN; - - boolean aSystemMax = aImportance >= NotificationManager.IMPORTANCE_HIGH && - isSystemNotification(na); - boolean bSystemMax = bImportance >= NotificationManager.IMPORTANCE_HIGH && - isSystemNotification(nb); - - boolean isHeadsUp = a.row.isHeadsUp(); - if (isHeadsUp != b.row.isHeadsUp()) { - return isHeadsUp ? -1 : 1; - } else if (isHeadsUp) { - // Provide consistent ranking with headsUpManager - return mHeadsUpManager.compare(a, b); - } else if (a.row.isAmbientPulsing() != b.row.isAmbientPulsing()) { - return a.row.isAmbientPulsing() ? -1 : 1; - } else if (aMedia != bMedia) { - // Upsort current media notification. - return aMedia ? -1 : 1; - } else if (aSystemMax != bSystemMax) { - // Upsort PRIORITY_MAX system notifications - return aSystemMax ? -1 : 1; - } else if (aRank != bRank) { - return aRank - bRank; - } else { - return Long.compare(nb.getNotification().when, na.getNotification().when); - } - } - }; - - private KeyguardEnvironment getEnvironment() { - if (mEnvironment == null) { - mEnvironment = Dependency.get(KeyguardEnvironment.class); - } - return mEnvironment; - } - - private NotificationMediaManager getMediaManager() { - if (mMediaManager == null) { - mMediaManager = Dependency.get(NotificationMediaManager.class); - } - return mMediaManager; - } - - /** - * Returns the sorted list of active notifications (depending on {@link KeyguardEnvironment} - * - * <p> - * This call doesn't update the list of active notifications. Call {@link #filterAndSort()} - * when the environment changes. - * <p> - * Don't hold on to or modify the returned list. - */ - public ArrayList<Entry> getActiveNotifications() { - return mSortedAndFiltered; - } - - public ArrayList<Entry> getNotificationsForCurrentUser() { - mFilteredForUser.clear(); - - synchronized (mEntries) { - final int N = mEntries.size(); - for (int i = 0; i < N; i++) { - Entry entry = mEntries.valueAt(i); - final StatusBarNotification sbn = entry.notification; - if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) { - continue; - } - mFilteredForUser.add(entry); - } - } - return mFilteredForUser; - } - - public Entry get(String key) { - return mEntries.get(key); - } - - public void add(Entry entry) { - synchronized (mEntries) { - mEntries.put(entry.notification.getKey(), entry); - } - mGroupManager.onEntryAdded(entry); - - updateRankingAndSort(mRankingMap); - } - - public Entry remove(String key, RankingMap ranking) { - Entry removed; - synchronized (mEntries) { - removed = mEntries.remove(key); - } - if (removed == null) return null; - mGroupManager.onEntryRemoved(removed); - updateRankingAndSort(ranking); - return removed; - } - - /** Updates the given notification entry with the provided ranking. */ - public void update(Entry entry, RankingMap ranking, StatusBarNotification notification) { - updateRanking(ranking); - final StatusBarNotification oldNotification = entry.notification; - entry.notification = notification; - mGroupManager.onEntryUpdated(entry, oldNotification); - } - - public void updateRanking(RankingMap ranking) { - updateRankingAndSort(ranking); - } - - public void updateAppOp(int appOp, int uid, String pkg, String key, boolean showIcon) { - synchronized (mEntries) { - final int N = mEntries.size(); - for (int i = 0; i < N; i++) { - Entry entry = mEntries.valueAt(i); - if (uid == entry.notification.getUid() - && pkg.equals(entry.notification.getPackageName()) - && key.equals(entry.key)) { - if (showIcon) { - entry.mActiveAppOps.add(appOp); - } else { - entry.mActiveAppOps.remove(appOp); - } - } - } - } - } - - /** - * Returns true if this notification should be displayed in the high-priority notifications - * section (and on the lockscreen and status bar). - */ - public boolean isHighPriority(StatusBarNotification statusBarNotification) { - if (mRankingMap != null) { - getRanking(statusBarNotification.getKey(), mTmpRanking); - if (mTmpRanking.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT - || statusBarNotification.getNotification().isForegroundService() - || statusBarNotification.getNotification().hasMediaSession()) { - return true; - } - if (mGroupManager.isSummaryOfGroup(statusBarNotification)) { - for (Entry child : mGroupManager.getLogicalChildren(statusBarNotification)) { - if (isHighPriority(child.notification)) { - return true; - } - } - } - } - return false; - } - - public boolean isAmbient(String key) { - if (mRankingMap != null) { - getRanking(key, mTmpRanking); - return mTmpRanking.isAmbient(); - } - return false; - } - - public int getVisibilityOverride(String key) { - if (mRankingMap != null) { - getRanking(key, mTmpRanking); - return mTmpRanking.getVisibilityOverride(); - } - return Ranking.VISIBILITY_NO_OVERRIDE; - } - - /** - * Categories that are explicitly called out on DND settings screens are always blocked, if - * DND has flagged them, even if they are foreground or system notifications that might - * otherwise visually bypass DND. - */ - private static boolean isNotificationBlockedByPolicy(Notification n) { - if (isCategory(CATEGORY_CALL, n) - || isCategory(CATEGORY_MESSAGE, n) - || isCategory(CATEGORY_ALARM, n) - || isCategory(CATEGORY_EVENT, n) - || isCategory(CATEGORY_REMINDER, n)) { - return true; - } - return false; - } - - private static boolean isCategory(String category, Notification n) { - return Objects.equals(n.category, category); - } - - public int getImportance(String key) { - if (mRankingMap != null) { - getRanking(key, mTmpRanking); - return mTmpRanking.getImportance(); - } - return NotificationManager.IMPORTANCE_UNSPECIFIED; - } - - public String getOverrideGroupKey(String key) { - if (mRankingMap != null) { - getRanking(key, mTmpRanking); - return mTmpRanking.getOverrideGroupKey(); - } - return null; - } - - public List<SnoozeCriterion> getSnoozeCriteria(String key) { - if (mRankingMap != null) { - getRanking(key, mTmpRanking); - return mTmpRanking.getSnoozeCriteria(); - } - return null; - } - - public NotificationChannel getChannel(String key) { - if (mRankingMap != null) { - getRanking(key, mTmpRanking); - return mTmpRanking.getChannel(); - } - return null; - } - - public int getRank(String key) { - if (mRankingMap != null) { - getRanking(key, mTmpRanking); - return mTmpRanking.getRank(); - } - return 0; - } - - public boolean shouldHide(String key) { - if (mRankingMap != null) { - getRanking(key, mTmpRanking); - return mTmpRanking.isSuspended(); - } - return false; - } - - private void updateRankingAndSort(RankingMap ranking) { - if (ranking != null) { - mRankingMap = ranking; - synchronized (mEntries) { - final int N = mEntries.size(); - for (int i = 0; i < N; i++) { - Entry entry = mEntries.valueAt(i); - if (!getRanking(entry.key, mTmpRanking)) { - continue; - } - final StatusBarNotification oldSbn = entry.notification.cloneLight(); - final String overrideGroupKey = getOverrideGroupKey(entry.key); - if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) { - entry.notification.setOverrideGroupKey(overrideGroupKey); - mGroupManager.onEntryUpdated(entry, oldSbn); - } - entry.populateFromRanking(mTmpRanking); - } - } - } - filterAndSort(); - } - - /** - * Get the ranking from the current ranking map. - * - * @param key the key to look up - * @param outRanking the ranking to populate - * - * @return {@code true} if the ranking was properly obtained. - */ - @VisibleForTesting - protected boolean getRanking(String key, Ranking outRanking) { - return mRankingMap.getRanking(key, outRanking); - } - - // TODO: This should not be public. Instead the Environment should notify this class when - // anything changed, and this class should call back the UI so it updates itself. - public void filterAndSort() { - mSortedAndFiltered.clear(); - - synchronized (mEntries) { - final int N = mEntries.size(); - for (int i = 0; i < N; i++) { - Entry entry = mEntries.valueAt(i); - - if (mNotificationFilter.shouldFilterOut(entry)) { - continue; - } - - mSortedAndFiltered.add(entry); - } - } - - Collections.sort(mSortedAndFiltered, mRankingComparator); - } - - public void dump(PrintWriter pw, String indent) { - int N = mSortedAndFiltered.size(); - pw.print(indent); - pw.println("active notifications: " + N); - int active; - for (active = 0; active < N; active++) { - NotificationData.Entry e = mSortedAndFiltered.get(active); - dumpEntry(pw, indent, active, e); - } - synchronized (mEntries) { - int M = mEntries.size(); - pw.print(indent); - pw.println("inactive notifications: " + (M - active)); - int inactiveCount = 0; - for (int i = 0; i < M; i++) { - Entry entry = mEntries.valueAt(i); - if (!mSortedAndFiltered.contains(entry)) { - dumpEntry(pw, indent, inactiveCount, entry); - inactiveCount++; - } - } - } - } - - private void dumpEntry(PrintWriter pw, String indent, int i, Entry e) { - getRanking(e.key, mTmpRanking); - pw.print(indent); - pw.println(" [" + i + "] key=" + e.key + " icon=" + e.icon); - StatusBarNotification n = e.notification; - pw.print(indent); - pw.println(" pkg=" + n.getPackageName() + " id=" + n.getId() + " importance=" + - mTmpRanking.getImportance()); - pw.print(indent); - pw.println(" notification=" + n.getNotification()); - } - - private static boolean isSystemNotification(StatusBarNotification sbn) { - String sbnPackage = sbn.getPackageName(); - return "android".equals(sbnPackage) || "com.android.systemui".equals(sbnPackage); - } - - /** - * Provides access to keyguard state and user settings dependent data. - */ - public interface KeyguardEnvironment { - boolean isDeviceProvisioned(); - boolean isNotificationForCurrentProfiles(StatusBarNotification sbn); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java index 2f60f115ed53..c6407604290f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java @@ -19,6 +19,7 @@ import android.annotation.Nullable; import android.service.notification.StatusBarNotification; import com.android.internal.statusbar.NotificationVisibility; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationInflater; /** @@ -29,25 +30,25 @@ public interface NotificationEntryListener { * Called when a new notification is posted. At this point, the notification is "pending": its * views haven't been inflated yet and most of the system pretends like it doesn't exist yet. */ - default void onPendingEntryAdded(NotificationData.Entry entry) { + default void onPendingEntryAdded(NotificationEntry entry) { } /** * Called when a new entry is created. */ - default void onNotificationAdded(NotificationData.Entry entry) { + default void onNotificationAdded(NotificationEntry entry) { } /** * Called when a notification was updated. */ - default void onEntryUpdated(NotificationData.Entry entry) { + default void onEntryUpdated(NotificationEntry entry) { } /** * Called when a notification's views are inflated for the first time. */ - default void onEntryInflated(NotificationData.Entry entry, + default void onEntryInflated(NotificationEntry entry, @NotificationInflater.InflationFlag int inflatedFlags) { } @@ -57,7 +58,7 @@ public interface NotificationEntryListener { * * @param entry notification data entry that was reinflated. */ - default void onEntryReinflated(NotificationData.Entry entry) { + default void onEntryReinflated(NotificationEntry entry) { } /** @@ -77,7 +78,7 @@ public interface NotificationEntryListener { * @param removedByUser true if the notification was removed by a user action */ default void onEntryRemoved( - NotificationData.Entry entry, + NotificationEntry entry, @Nullable NotificationVisibility visibility, boolean removedByUser) { } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index 6b5e70825d1d..a4e3f339b58f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -36,7 +36,9 @@ import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationUiAdjustment; import com.android.systemui.statusbar.NotificationUpdateHandler; -import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment; +import com.android.systemui.statusbar.notification.collection.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationInflater; import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag; @@ -68,7 +70,7 @@ public class NotificationEntryManager implements public static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30); protected final Context mContext; - protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>(); + protected final HashMap<String, NotificationEntry> mPendingNotifications = new HashMap<>(); private final NotificationGutsManager mGutsManager = Dependency.get(NotificationGutsManager.class); @@ -113,7 +115,7 @@ public class NotificationEntryManager implements if (mPendingNotifications.size() == 0) { pw.println("null"); } else { - for (NotificationData.Entry entry : mPendingNotifications.values()) { + for (NotificationEntry entry : mPendingNotifications.values()) { pw.println(entry.notification); } } @@ -205,11 +207,11 @@ public class NotificationEntryManager implements private void abortExistingInflation(String key) { if (mPendingNotifications.containsKey(key)) { - NotificationData.Entry entry = mPendingNotifications.get(key); + NotificationEntry entry = mPendingNotifications.get(key); entry.abortTask(); mPendingNotifications.remove(key); } - NotificationData.Entry addedEntry = mNotificationData.get(key); + NotificationEntry addedEntry = mNotificationData.get(key); if (addedEntry != null) { addedEntry.abortTask(); } @@ -230,7 +232,7 @@ public class NotificationEntryManager implements } } - private void maybeScheduleUpdateNotificationViews(NotificationData.Entry entry) { + private void maybeScheduleUpdateNotificationViews(NotificationEntry entry) { long audibleAlertTimeout = RECENTLY_ALERTED_THRESHOLD_MS - (System.currentTimeMillis() - entry.lastAudiblyAlertedMs); if (audibleAlertTimeout > 0) { @@ -240,7 +242,7 @@ public class NotificationEntryManager implements } @Override - public void onAsyncInflationFinished(NotificationData.Entry entry, + public void onAsyncInflationFinished(NotificationEntry entry, @InflationFlag int inflatedFlags) { mPendingNotifications.remove(entry.key); // If there was an async task started after the removal, we don't want to add it back to @@ -279,7 +281,7 @@ public class NotificationEntryManager implements @Nullable NotificationVisibility visibility, boolean forceRemove, boolean removedByUser) { - final NotificationData.Entry entry = mNotificationData.get(key); + final NotificationEntry entry = mNotificationData.get(key); abortExistingInflation(key); @@ -337,19 +339,19 @@ public class NotificationEntryManager implements * */ private void handleGroupSummaryRemoved(String key) { - NotificationData.Entry entry = mNotificationData.get(key); + NotificationEntry entry = mNotificationData.get(key); if (entry != null && entry.rowExists() && entry.isSummaryWithChildren()) { if (entry.notification.getOverrideGroupKey() != null && !entry.isRowDismissed()) { // We don't want to remove children for autobundled notifications as they are not // always cancelled. We only remove them if they were dismissed by the user. return; } - List<NotificationData.Entry> childEntries = entry.getChildren(); + List<NotificationEntry> childEntries = entry.getChildren(); if (childEntries == null) { return; } for (int i = 0; i < childEntries.size(); i++) { - NotificationData.Entry childEntry = childEntries.get(i); + NotificationEntry childEntry = childEntries.get(i); boolean isForeground = (entry.notification.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0; boolean keepForReply = @@ -369,10 +371,10 @@ public class NotificationEntryManager implements } public void updateNotificationsOnDensityOrFontScaleChanged() { - ArrayList<NotificationData.Entry> userNotifications = + ArrayList<NotificationEntry> userNotifications = mNotificationData.getNotificationsForCurrentUser(); for (int i = 0; i < userNotifications.size(); i++) { - NotificationData.Entry entry = userNotifications.get(i); + NotificationEntry entry = userNotifications.get(i); entry.onDensityOrFontScaleChanged(); boolean exposedGuts = entry.areGutsExposed(); if (exposedGuts) { @@ -392,7 +394,7 @@ public class NotificationEntryManager implements NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking(); rankingMap.getRanking(key, ranking); - NotificationData.Entry entry = new NotificationData.Entry(notification, ranking); + NotificationEntry entry = new NotificationEntry(notification, ranking); Dependency.get(LeakDetector.class).trackInstance(entry); // Construct the expanded view. @@ -445,7 +447,7 @@ public class NotificationEntryManager implements final String key = notification.getKey(); abortExistingInflation(key); - NotificationData.Entry entry = mNotificationData.get(key); + NotificationEntry entry = mNotificationData.get(key); if (entry == null) { return; } @@ -494,14 +496,14 @@ public class NotificationEntryManager implements } public void updateNotificationRanking(NotificationListenerService.RankingMap rankingMap) { - List<NotificationData.Entry> entries = new ArrayList<>(); + List<NotificationEntry> entries = new ArrayList<>(); entries.addAll(mNotificationData.getActiveNotifications()); entries.addAll(mPendingNotifications.values()); // Has a copy of the current UI adjustments. ArrayMap<String, NotificationUiAdjustment> oldAdjustments = new ArrayMap<>(); ArrayMap<String, Integer> oldImportances = new ArrayMap<>(); - for (NotificationData.Entry entry : entries) { + for (NotificationEntry entry : entries) { NotificationUiAdjustment adjustment = NotificationUiAdjustment.extractFromNotificationEntry(entry); oldAdjustments.put(entry.key, adjustment); @@ -513,7 +515,7 @@ public class NotificationEntryManager implements updateRankingOfPendingNotifications(rankingMap); // By comparing the old and new UI adjustments, reinflate the view accordingly. - for (NotificationData.Entry entry : entries) { + for (NotificationEntry entry : entries) { getRowBinder().onNotificationRankingUpdated( entry, oldImportances.get(entry.key), @@ -531,7 +533,7 @@ public class NotificationEntryManager implements return; } NotificationListenerService.Ranking tmpRanking = new NotificationListenerService.Ranking(); - for (NotificationData.Entry pendingNotification : mPendingNotifications.values()) { + for (NotificationEntry pendingNotification : mPendingNotifications.values()) { rankingMap.getRanking(pendingNotification.key, tmpRanking); pendingNotification.populateFromRanking(tmpRanking); } @@ -542,7 +544,7 @@ public class NotificationEntryManager implements * notifications whose views have not yet been inflated. In general, the system pretends like * these don't exist, although there are a couple exceptions. */ - public Iterable<NotificationData.Entry> getPendingNotificationsIterator() { + public Iterable<NotificationEntry> getPendingNotificationsIterator() { return mPendingNotifications.values(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java index 700382a103b2..e199ead18a68 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java @@ -28,6 +28,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; import com.android.systemui.ForegroundServiceController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.notification.collection.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; @@ -82,7 +84,7 @@ public class NotificationFilter { /** * @return true if the provided notification should NOT be shown right now. */ - public boolean shouldFilterOut(NotificationData.Entry entry) { + public boolean shouldFilterOut(NotificationEntry entry) { final StatusBarNotification sbn = entry.notification; if (!(getEnvironment().isDeviceProvisioned() || showNotificationEvenIfUnprovisioned(sbn))) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java index 8bd0e9ad3c8b..fc7a2b37eca7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java @@ -37,6 +37,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -139,7 +140,7 @@ public class NotificationInterruptionStateProvider { * @param entry the entry to check * @return true if the entry should heads up, false otherwise */ - public boolean shouldHeadsUp(NotificationData.Entry entry) { + public boolean shouldHeadsUp(NotificationEntry entry) { StatusBarNotification sbn = entry.notification; if (getShadeController().isDozing()) { @@ -227,7 +228,7 @@ public class NotificationInterruptionStateProvider { * @param entry the entry to check * @return true if the entry should ambient pulse, false otherwise */ - public boolean shouldPulse(NotificationData.Entry entry) { + public boolean shouldPulse(NotificationEntry entry) { StatusBarNotification sbn = entry.notification; if (!getShadeController().isDozing()) { @@ -273,14 +274,14 @@ public class NotificationInterruptionStateProvider { /** * Common checks between heads up alerting and ambient pulse alerting. See - * {@link #shouldHeadsUp(NotificationData.Entry)} and - * {@link #shouldPulse(NotificationData.Entry)}. Notifications that fail any of these checks + * {@link #shouldHeadsUp(NotificationEntry)} and + * {@link #shouldPulse(NotificationEntry)}. Notifications that fail any of these checks * should not alert at all. * * @param entry the entry to check * @return true if these checks pass, false if the notification should not alert */ - protected boolean canAlertCommon(NotificationData.Entry entry) { + protected boolean canAlertCommon(NotificationEntry entry) { StatusBarNotification sbn = entry.notification; if (mNotificationFilter.shouldFilterOut(entry)) { @@ -325,7 +326,7 @@ public class NotificationInterruptionStateProvider { * @param sbn notification that might be heads upped * @return false if the notification can not be heads upped */ - boolean canHeadsUp(NotificationData.Entry entry, StatusBarNotification sbn); + boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java index 058efca51b21..cc302b16f7c1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java @@ -42,6 +42,7 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationUiAdjustment; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationInflater; @@ -126,7 +127,7 @@ public class NotificationRowBinder { * Inflates the views for the given entry (possibly asynchronously). */ public void inflateViews( - NotificationData.Entry entry, + NotificationEntry entry, Runnable onDismissRunnable, boolean isUpdate) throws InflationException { @@ -149,7 +150,7 @@ public class NotificationRowBinder { } } - private void bindRow(NotificationData.Entry entry, PackageManager pmUser, + private void bindRow(NotificationEntry entry, PackageManager pmUser, StatusBarNotification sbn, ExpandableNotificationRow row, Runnable onDismissRunnable) { row.setExpansionLogger(mExpansionLogger, entry.notification.getKey()); @@ -197,7 +198,7 @@ public class NotificationRowBinder { * reinflating them. */ public void onNotificationRankingUpdated( - NotificationData.Entry entry, + NotificationEntry entry, @Nullable Integer oldImportance, NotificationUiAdjustment oldAdjustment, NotificationUiAdjustment newAdjustment, @@ -224,7 +225,7 @@ public class NotificationRowBinder { //TODO: This method associates a row with an entry, but eventually needs to not do that private void updateNotification( - NotificationData.Entry entry, + NotificationEntry entry, PackageManager pmUser, StatusBarNotification sbn, ExpandableNotificationRow row, @@ -292,7 +293,7 @@ public class NotificationRowBinder { * @param sbn notification * @param row row for the notification */ - void onBindRow(NotificationData.Entry entry, PackageManager pmUser, + void onBindRow(NotificationEntry entry, PackageManager pmUser, StatusBarNotification sbn, ExpandableNotificationRow row); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java index a194eef39b6d..f09c57d786ef 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.notification; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; + /** * An object that can determine the visibility of a Notification. */ @@ -27,5 +29,5 @@ public interface VisibilityLocationProvider { * @param entry * @return true if row is in a visible location */ - boolean isInVisibleLocation(NotificationData.Entry entry); + boolean isInVisibleLocation(NotificationEntry entry); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java index 8e6a93deec69..c886685424f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java @@ -21,6 +21,7 @@ import android.view.View; import androidx.collection.ArraySet; import com.android.systemui.statusbar.NotificationPresenter; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; @@ -52,7 +53,7 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener { public VisualStabilityManager(NotificationEntryManager notificationEntryManager) { notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() { @Override - public void onEntryReinflated(NotificationData.Entry entry) { + public void onEntryReinflated(NotificationEntry entry) { if (entry.hasLowPriorityStateUpdated()) { onLowPriorityUpdated(entry); if (mPresenter != null) { @@ -163,7 +164,7 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener { } @Override - public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) { + public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { if (isHeadsUp) { // Heads up notifications should in general be allowed to reorder if they are out of // view and stay at the current location if they aren't. @@ -171,7 +172,7 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener { } } - private void onLowPriorityUpdated(NotificationData.Entry entry) { + private void onLowPriorityUpdated(NotificationEntry entry) { mLowPriorityReorderingViews.add(entry.getRow()); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java new file mode 100644 index 000000000000..8c29fb50f00a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java @@ -0,0 +1,424 @@ +/* + * Copyright (C) 2019 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.systemui.statusbar.notification.collection; + +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.service.notification.NotificationListenerService.Ranking; +import android.service.notification.NotificationListenerService.RankingMap; +import android.service.notification.SnoozeCriterion; +import android.service.notification.StatusBarNotification; +import android.util.ArrayMap; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.Dependency; +import com.android.systemui.statusbar.NotificationMediaManager; +import com.android.systemui.statusbar.notification.NotificationFilter; +import com.android.systemui.statusbar.phone.NotificationGroupManager; +import com.android.systemui.statusbar.policy.HeadsUpManager; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; + +/** + * The list of currently displaying notifications. + */ +public class NotificationData { + + private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class); + + /** + * These dependencies are late init-ed + */ + private KeyguardEnvironment mEnvironment; + private NotificationMediaManager mMediaManager; + + private HeadsUpManager mHeadsUpManager; + + private final ArrayMap<String, NotificationEntry> mEntries = new ArrayMap<>(); + private final ArrayList<NotificationEntry> mSortedAndFiltered = new ArrayList<>(); + private final ArrayList<NotificationEntry> mFilteredForUser = new ArrayList<>(); + + private final NotificationGroupManager mGroupManager = + Dependency.get(NotificationGroupManager.class); + + private RankingMap mRankingMap; + private final Ranking mTmpRanking = new Ranking(); + + public void setHeadsUpManager(HeadsUpManager headsUpManager) { + mHeadsUpManager = headsUpManager; + } + + private final Comparator<NotificationEntry> mRankingComparator = + new Comparator<NotificationEntry>() { + private final Ranking mRankingA = new Ranking(); + private final Ranking mRankingB = new Ranking(); + + @Override + public int compare(NotificationEntry a, NotificationEntry b) { + final StatusBarNotification na = a.notification; + final StatusBarNotification nb = b.notification; + int aImportance = NotificationManager.IMPORTANCE_DEFAULT; + int bImportance = NotificationManager.IMPORTANCE_DEFAULT; + int aRank = 0; + int bRank = 0; + + if (mRankingMap != null) { + // RankingMap as received from NoMan + getRanking(a.key, mRankingA); + getRanking(b.key, mRankingB); + aImportance = mRankingA.getImportance(); + bImportance = mRankingB.getImportance(); + aRank = mRankingA.getRank(); + bRank = mRankingB.getRank(); + } + + String mediaNotification = getMediaManager().getMediaNotificationKey(); + + // IMPORTANCE_MIN media streams are allowed to drift to the bottom + final boolean aMedia = a.key.equals(mediaNotification) + && aImportance > NotificationManager.IMPORTANCE_MIN; + final boolean bMedia = b.key.equals(mediaNotification) + && bImportance > NotificationManager.IMPORTANCE_MIN; + + boolean aSystemMax = aImportance >= NotificationManager.IMPORTANCE_HIGH + && isSystemNotification(na); + boolean bSystemMax = bImportance >= NotificationManager.IMPORTANCE_HIGH + && isSystemNotification(nb); + + boolean isHeadsUp = a.getRow().isHeadsUp(); + if (isHeadsUp != b.getRow().isHeadsUp()) { + return isHeadsUp ? -1 : 1; + } else if (isHeadsUp) { + // Provide consistent ranking with headsUpManager + return mHeadsUpManager.compare(a, b); + } else if (a.getRow().isAmbientPulsing() != b.getRow().isAmbientPulsing()) { + return a.getRow().isAmbientPulsing() ? -1 : 1; + } else if (aMedia != bMedia) { + // Upsort current media notification. + return aMedia ? -1 : 1; + } else if (aSystemMax != bSystemMax) { + // Upsort PRIORITY_MAX system notifications + return aSystemMax ? -1 : 1; + } else if (aRank != bRank) { + return aRank - bRank; + } else { + return Long.compare(nb.getNotification().when, na.getNotification().when); + } + } + }; + + private KeyguardEnvironment getEnvironment() { + if (mEnvironment == null) { + mEnvironment = Dependency.get(KeyguardEnvironment.class); + } + return mEnvironment; + } + + private NotificationMediaManager getMediaManager() { + if (mMediaManager == null) { + mMediaManager = Dependency.get(NotificationMediaManager.class); + } + return mMediaManager; + } + + /** + * Returns the sorted list of active notifications (depending on {@link KeyguardEnvironment} + * + * <p> + * This call doesn't update the list of active notifications. Call {@link #filterAndSort()} + * when the environment changes. + * <p> + * Don't hold on to or modify the returned list. + */ + public ArrayList<NotificationEntry> getActiveNotifications() { + return mSortedAndFiltered; + } + + public ArrayList<NotificationEntry> getNotificationsForCurrentUser() { + mFilteredForUser.clear(); + + synchronized (mEntries) { + final int len = mEntries.size(); + for (int i = 0; i < len; i++) { + NotificationEntry entry = mEntries.valueAt(i); + final StatusBarNotification sbn = entry.notification; + if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) { + continue; + } + mFilteredForUser.add(entry); + } + } + return mFilteredForUser; + } + + public NotificationEntry get(String key) { + return mEntries.get(key); + } + + public void add(NotificationEntry entry) { + synchronized (mEntries) { + mEntries.put(entry.notification.getKey(), entry); + } + mGroupManager.onEntryAdded(entry); + + updateRankingAndSort(mRankingMap); + } + + public NotificationEntry remove(String key, RankingMap ranking) { + NotificationEntry removed; + synchronized (mEntries) { + removed = mEntries.remove(key); + } + if (removed == null) return null; + mGroupManager.onEntryRemoved(removed); + updateRankingAndSort(ranking); + return removed; + } + + /** Updates the given notification entry with the provided ranking. */ + public void update( + NotificationEntry entry, + RankingMap ranking, + StatusBarNotification notification) { + updateRanking(ranking); + final StatusBarNotification oldNotification = entry.notification; + entry.notification = notification; + mGroupManager.onEntryUpdated(entry, oldNotification); + } + + public void updateRanking(RankingMap ranking) { + updateRankingAndSort(ranking); + } + + public void updateAppOp(int appOp, int uid, String pkg, String key, boolean showIcon) { + synchronized (mEntries) { + final int len = mEntries.size(); + for (int i = 0; i < len; i++) { + NotificationEntry entry = mEntries.valueAt(i); + if (uid == entry.notification.getUid() + && pkg.equals(entry.notification.getPackageName()) + && key.equals(entry.key)) { + if (showIcon) { + entry.mActiveAppOps.add(appOp); + } else { + entry.mActiveAppOps.remove(appOp); + } + } + } + } + } + + /** + * Returns true if this notification should be displayed in the high-priority notifications + * section (and on the lockscreen and status bar). + */ + public boolean isHighPriority(StatusBarNotification statusBarNotification) { + if (mRankingMap != null) { + getRanking(statusBarNotification.getKey(), mTmpRanking); + if (mTmpRanking.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT + || statusBarNotification.getNotification().isForegroundService() + || statusBarNotification.getNotification().hasMediaSession()) { + return true; + } + if (mGroupManager.isSummaryOfGroup(statusBarNotification)) { + final ArrayList<NotificationEntry> logicalChildren = + mGroupManager.getLogicalChildren(statusBarNotification); + for (NotificationEntry child : logicalChildren) { + if (isHighPriority(child.notification)) { + return true; + } + } + } + } + return false; + } + + public boolean isAmbient(String key) { + if (mRankingMap != null) { + getRanking(key, mTmpRanking); + return mTmpRanking.isAmbient(); + } + return false; + } + + public int getVisibilityOverride(String key) { + if (mRankingMap != null) { + getRanking(key, mTmpRanking); + return mTmpRanking.getVisibilityOverride(); + } + return Ranking.VISIBILITY_NO_OVERRIDE; + } + + public int getImportance(String key) { + if (mRankingMap != null) { + getRanking(key, mTmpRanking); + return mTmpRanking.getImportance(); + } + return NotificationManager.IMPORTANCE_UNSPECIFIED; + } + + public String getOverrideGroupKey(String key) { + if (mRankingMap != null) { + getRanking(key, mTmpRanking); + return mTmpRanking.getOverrideGroupKey(); + } + return null; + } + + public List<SnoozeCriterion> getSnoozeCriteria(String key) { + if (mRankingMap != null) { + getRanking(key, mTmpRanking); + return mTmpRanking.getSnoozeCriteria(); + } + return null; + } + + public NotificationChannel getChannel(String key) { + if (mRankingMap != null) { + getRanking(key, mTmpRanking); + return mTmpRanking.getChannel(); + } + return null; + } + + public int getRank(String key) { + if (mRankingMap != null) { + getRanking(key, mTmpRanking); + return mTmpRanking.getRank(); + } + return 0; + } + + public boolean shouldHide(String key) { + if (mRankingMap != null) { + getRanking(key, mTmpRanking); + return mTmpRanking.isSuspended(); + } + return false; + } + + private void updateRankingAndSort(RankingMap ranking) { + if (ranking != null) { + mRankingMap = ranking; + synchronized (mEntries) { + final int len = mEntries.size(); + for (int i = 0; i < len; i++) { + NotificationEntry entry = mEntries.valueAt(i); + if (!getRanking(entry.key, mTmpRanking)) { + continue; + } + final StatusBarNotification oldSbn = entry.notification.cloneLight(); + final String overrideGroupKey = getOverrideGroupKey(entry.key); + if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) { + entry.notification.setOverrideGroupKey(overrideGroupKey); + mGroupManager.onEntryUpdated(entry, oldSbn); + } + entry.populateFromRanking(mTmpRanking); + } + } + } + filterAndSort(); + } + + /** + * Get the ranking from the current ranking map. + * + * @param key the key to look up + * @param outRanking the ranking to populate + * + * @return {@code true} if the ranking was properly obtained. + */ + @VisibleForTesting + protected boolean getRanking(String key, Ranking outRanking) { + return mRankingMap.getRanking(key, outRanking); + } + + // TODO: This should not be public. Instead the Environment should notify this class when + // anything changed, and this class should call back the UI so it updates itself. + public void filterAndSort() { + mSortedAndFiltered.clear(); + + synchronized (mEntries) { + final int len = mEntries.size(); + for (int i = 0; i < len; i++) { + NotificationEntry entry = mEntries.valueAt(i); + + if (mNotificationFilter.shouldFilterOut(entry)) { + continue; + } + + mSortedAndFiltered.add(entry); + } + } + + Collections.sort(mSortedAndFiltered, mRankingComparator); + } + + public void dump(PrintWriter pw, String indent) { + int filteredLen = mSortedAndFiltered.size(); + pw.print(indent); + pw.println("active notifications: " + filteredLen); + int active; + for (active = 0; active < filteredLen; active++) { + NotificationEntry e = mSortedAndFiltered.get(active); + dumpEntry(pw, indent, active, e); + } + synchronized (mEntries) { + int totalLen = mEntries.size(); + pw.print(indent); + pw.println("inactive notifications: " + (totalLen - active)); + int inactiveCount = 0; + for (int i = 0; i < totalLen; i++) { + NotificationEntry entry = mEntries.valueAt(i); + if (!mSortedAndFiltered.contains(entry)) { + dumpEntry(pw, indent, inactiveCount, entry); + inactiveCount++; + } + } + } + } + + private void dumpEntry(PrintWriter pw, String indent, int i, NotificationEntry e) { + getRanking(e.key, mTmpRanking); + pw.print(indent); + pw.println(" [" + i + "] key=" + e.key + " icon=" + e.icon); + StatusBarNotification n = e.notification; + pw.print(indent); + pw.println(" pkg=" + n.getPackageName() + " id=" + n.getId() + " importance=" + + mTmpRanking.getImportance()); + pw.print(indent); + pw.println(" notification=" + n.getNotification()); + } + + private static boolean isSystemNotification(StatusBarNotification sbn) { + String sbnPackage = sbn.getPackageName(); + return "android".equals(sbnPackage) || "com.android.systemui".equals(sbnPackage); + } + + /** + * Provides access to keyguard state and user settings dependent data. + */ + public interface KeyguardEnvironment { + boolean isDeviceProvisioned(); + boolean isNotificationForCurrentProfiles(StatusBarNotification sbn); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java new file mode 100644 index 000000000000..58aa02ccf440 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -0,0 +1,701 @@ +/* + * Copyright (C) 2019 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.systemui.statusbar.notification.collection; + +import static android.app.Notification.CATEGORY_ALARM; +import static android.app.Notification.CATEGORY_CALL; +import static android.app.Notification.CATEGORY_EVENT; +import static android.app.Notification.CATEGORY_MESSAGE; +import static android.app.Notification.CATEGORY_REMINDER; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; +import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; + +import android.annotation.NonNull; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager.Policy; +import android.app.Person; +import android.content.Context; +import android.graphics.drawable.Icon; +import android.os.Bundle; +import android.os.Parcelable; +import android.os.SystemClock; +import android.service.notification.NotificationListenerService; +import android.service.notification.SnoozeCriterion; +import android.service.notification.StatusBarNotification; +import android.util.ArraySet; +import android.view.View; +import android.widget.ImageView; + +import androidx.annotation.Nullable; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.statusbar.StatusBarIcon; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.ContrastColorUtil; +import com.android.systemui.statusbar.InflationTask; +import com.android.systemui.statusbar.StatusBarIconView; +import com.android.systemui.statusbar.notification.InflationException; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.row.NotificationGuts; +import com.android.systemui.statusbar.notification.row.NotificationInflater; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * Represents a notification that the system UI knows about + * + * Whenever the NotificationManager tells us about the existence of a new notification, we wrap it + * in a NotificationEntry. Thus, every notification has an associated NotificationEntry, even if + * that notification is never displayed to the user (for example, if it's filtered out for some + * reason). + * + * Entries store information about the current state of the notification. Essentially: + * anything that needs to persist or be modifiable even when the notification's views don't + * exist. Any other state should be stored on the views/view controllers themselves. + * + * At the moment, there are many things here that shouldn't be and vice-versa. Hopefully we can + * clean this up in the future. + */ +public final class NotificationEntry { + private static final long LAUNCH_COOLDOWN = 2000; + private static final long REMOTE_INPUT_COOLDOWN = 500; + private static final long INITIALIZATION_DELAY = 400; + private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN; + private static final int COLOR_INVALID = 1; + public final String key; + public StatusBarNotification notification; + public NotificationChannel channel; + public long lastAudiblyAlertedMs; + public boolean noisy; + public boolean ambient; + public int importance; + public StatusBarIconView icon; + public StatusBarIconView expandedIcon; + private boolean interruption; + public boolean autoRedacted; // whether the redacted notification was generated by us + public int targetSdk; + private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET; + public CharSequence remoteInputText; + public List<SnoozeCriterion> snoozeCriteria; + public int userSentiment = NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL; + /** Smart Actions provided by the NotificationAssistantService. */ + @NonNull + public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList(); + public CharSequence[] smartReplies = new CharSequence[0]; + @VisibleForTesting + public int suppressedVisualEffects; + public boolean suspended; + + private NotificationEntry parent; // our parent (if we're in a group) + private ArrayList<NotificationEntry> children = new ArrayList<NotificationEntry>(); + private ExpandableNotificationRow row; // the outer expanded view + + private int mCachedContrastColor = COLOR_INVALID; + private int mCachedContrastColorIsFor = COLOR_INVALID; + private InflationTask mRunningTask = null; + private Throwable mDebugThrowable; + public CharSequence remoteInputTextWhenReset; + public long lastRemoteInputSent = NOT_LAUNCHED_YET; + public ArraySet<Integer> mActiveAppOps = new ArraySet<>(3); + public CharSequence headsUpStatusBarText; + public CharSequence headsUpStatusBarTextPublic; + + private long initializationTime = -1; + + /** + * Whether or not this row represents a system notification. Note that if this is + * {@code null}, that means we were either unable to retrieve the info or have yet to + * retrieve the info. + */ + public Boolean mIsSystemNotification; + + /** + * Has the user sent a reply through this Notification. + */ + private boolean hasSentReply; + + /** + * Whether this notification should be displayed as a bubble. + */ + private boolean mIsBubble; + + /** + * Whether the user has dismissed this notification when it was in bubble form. + */ + private boolean mUserDismissedBubble; + + public NotificationEntry(StatusBarNotification n) { + this(n, null); + } + + public NotificationEntry( + StatusBarNotification n, + @Nullable NotificationListenerService.Ranking ranking) { + this.key = n.getKey(); + this.notification = n; + if (ranking != null) { + populateFromRanking(ranking); + } + } + + public void populateFromRanking(@NonNull NotificationListenerService.Ranking ranking) { + channel = ranking.getChannel(); + lastAudiblyAlertedMs = ranking.getLastAudiblyAlertedMillis(); + importance = ranking.getImportance(); + ambient = ranking.isAmbient(); + snoozeCriteria = ranking.getSnoozeCriteria(); + userSentiment = ranking.getUserSentiment(); + systemGeneratedSmartActions = ranking.getSmartActions() == null + ? Collections.emptyList() : ranking.getSmartActions(); + smartReplies = ranking.getSmartReplies() == null + ? new CharSequence[0] + : ranking.getSmartReplies().toArray(new CharSequence[0]); + suppressedVisualEffects = ranking.getSuppressedVisualEffects(); + suspended = ranking.isSuspended(); + } + + public void setInterruption() { + interruption = true; + } + + public boolean hasInterrupted() { + return interruption; + } + + public void setIsBubble(boolean bubbleable) { + mIsBubble = bubbleable; + } + + public boolean isBubble() { + return mIsBubble; + } + + public void setBubbleDismissed(boolean userDismissed) { + mUserDismissedBubble = userDismissed; + } + + public boolean isBubbleDismissed() { + return mUserDismissedBubble; + } + + /** + * Resets the notification entry to be re-used. + */ + public void reset() { + if (row != null) { + row.reset(); + } + } + + public ExpandableNotificationRow getRow() { + return row; + } + + //TODO: This will go away when we have a way to bind an entry to a row + public void setRow(ExpandableNotificationRow row) { + this.row = row; + } + + @Nullable + public List<NotificationEntry> getChildren() { + if (children.size() <= 0) { + return null; + } + + return children; + } + + public void notifyFullScreenIntentLaunched() { + setInterruption(); + lastFullScreenIntentLaunchTime = SystemClock.elapsedRealtime(); + } + + public boolean hasJustLaunchedFullScreenIntent() { + return SystemClock.elapsedRealtime() < lastFullScreenIntentLaunchTime + LAUNCH_COOLDOWN; + } + + public boolean hasJustSentRemoteInput() { + return SystemClock.elapsedRealtime() < lastRemoteInputSent + REMOTE_INPUT_COOLDOWN; + } + + public boolean hasFinishedInitialization() { + return initializationTime == -1 + || SystemClock.elapsedRealtime() > initializationTime + INITIALIZATION_DELAY; + } + + /** + * Create the icons for a notification + * @param context the context to create the icons with + * @param sbn the notification + * @throws InflationException Exception if required icons are not valid or specified + */ + public void createIcons(Context context, StatusBarNotification sbn) + throws InflationException { + Notification n = sbn.getNotification(); + final Icon smallIcon = n.getSmallIcon(); + if (smallIcon == null) { + throw new InflationException("No small icon in notification from " + + sbn.getPackageName()); + } + + // Construct the icon. + icon = new StatusBarIconView(context, + sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn); + icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE); + + // Construct the expanded icon. + expandedIcon = new StatusBarIconView(context, + sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn); + expandedIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE); + final StatusBarIcon ic = new StatusBarIcon( + sbn.getUser(), + sbn.getPackageName(), + smallIcon, + n.iconLevel, + n.number, + StatusBarIconView.contentDescForNotification(context, n)); + if (!icon.set(ic) || !expandedIcon.set(ic)) { + icon = null; + expandedIcon = null; + throw new InflationException("Couldn't create icon: " + ic); + } + expandedIcon.setVisibility(View.INVISIBLE); + expandedIcon.setOnVisibilityChangedListener( + newVisibility -> { + if (row != null) { + row.setIconsVisible(newVisibility != View.VISIBLE); + } + }); + } + + public void setIconTag(int key, Object tag) { + if (icon != null) { + icon.setTag(key, tag); + expandedIcon.setTag(key, tag); + } + } + + /** + * Update the notification icons. + * + * @param context the context to create the icons with. + * @param sbn the notification to read the icon from. + * @throws InflationException Exception if required icons are not valid or specified + */ + public void updateIcons(Context context, StatusBarNotification sbn) + throws InflationException { + if (icon != null) { + // Update the icon + Notification n = sbn.getNotification(); + final StatusBarIcon ic = new StatusBarIcon( + notification.getUser(), + notification.getPackageName(), + n.getSmallIcon(), + n.iconLevel, + n.number, + StatusBarIconView.contentDescForNotification(context, n)); + icon.setNotification(sbn); + expandedIcon.setNotification(sbn); + if (!icon.set(ic) || !expandedIcon.set(ic)) { + throw new InflationException("Couldn't update icon: " + ic); + } + } + } + + public int getContrastedColor(Context context, boolean isLowPriority, + int backgroundColor) { + int rawColor = isLowPriority ? Notification.COLOR_DEFAULT : + notification.getNotification().color; + if (mCachedContrastColorIsFor == rawColor && mCachedContrastColor != COLOR_INVALID) { + return mCachedContrastColor; + } + final int contrasted = ContrastColorUtil.resolveContrastColor(context, rawColor, + backgroundColor); + mCachedContrastColorIsFor = rawColor; + mCachedContrastColor = contrasted; + return mCachedContrastColor; + } + + /** + * Abort all existing inflation tasks + */ + public void abortTask() { + if (mRunningTask != null) { + mRunningTask.abort(); + mRunningTask = null; + } + } + + public void setInflationTask(InflationTask abortableTask) { + // abort any existing inflation + InflationTask existing = mRunningTask; + abortTask(); + mRunningTask = abortableTask; + if (existing != null && mRunningTask != null) { + mRunningTask.supersedeTask(existing); + } + } + + public void onInflationTaskFinished() { + mRunningTask = null; + } + + @VisibleForTesting + public InflationTask getRunningTask() { + return mRunningTask; + } + + /** + * Set a throwable that is used for debugging + * + * @param debugThrowable the throwable to save + */ + public void setDebugThrowable(Throwable debugThrowable) { + mDebugThrowable = debugThrowable; + } + + public Throwable getDebugThrowable() { + return mDebugThrowable; + } + + public void onRemoteInputInserted() { + lastRemoteInputSent = NOT_LAUNCHED_YET; + remoteInputTextWhenReset = null; + } + + public void setHasSentReply() { + hasSentReply = true; + } + + public boolean isLastMessageFromReply() { + if (!hasSentReply) { + return false; + } + Bundle extras = notification.getNotification().extras; + CharSequence[] replyTexts = extras.getCharSequenceArray( + Notification.EXTRA_REMOTE_INPUT_HISTORY); + if (!ArrayUtils.isEmpty(replyTexts)) { + return true; + } + Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES); + if (messages != null && messages.length > 0) { + Parcelable message = messages[messages.length - 1]; + if (message instanceof Bundle) { + Notification.MessagingStyle.Message lastMessage = + Notification.MessagingStyle.Message.getMessageFromBundle( + (Bundle) message); + if (lastMessage != null) { + Person senderPerson = lastMessage.getSenderPerson(); + if (senderPerson == null) { + return true; + } + Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON); + return Objects.equals(user, senderPerson); + } + } + } + return false; + } + + public void setInitializationTime(long time) { + if (initializationTime == -1) { + initializationTime = time; + } + } + + public void sendAccessibilityEvent(int eventType) { + if (row != null) { + row.sendAccessibilityEvent(eventType); + } + } + + /** + * Used by NotificationMediaManager to determine... things + * @return {@code true} if we are a media notification + */ + public boolean isMediaNotification() { + if (row == null) return false; + + return row.isMediaRow(); + } + + /** + * We are a top level child if our parent is the list of notifications duh + * @return {@code true} if we're a top level notification + */ + public boolean isTopLevelChild() { + return row != null && row.isTopLevelChild(); + } + + public void resetUserExpansion() { + if (row != null) row.resetUserExpansion(); + } + + public void freeContentViewWhenSafe(@NotificationInflater.InflationFlag int inflationFlag) { + if (row != null) row.freeContentViewWhenSafe(inflationFlag); + } + + public void setAmbientPulsing(boolean pulsing) { + if (row != null) row.setAmbientPulsing(pulsing); + } + + public boolean rowExists() { + return row != null; + } + + public boolean isRowDismissed() { + return row != null && row.isDismissed(); + } + + public boolean isRowRemoved() { + return row != null && row.isRemoved(); + } + + /** + * @return {@code true} if the row is null or removed + */ + public boolean isRemoved() { + //TODO: recycling invalidates this + return row == null || row.isRemoved(); + } + + /** + * @return {@code true} if the row is null or dismissed + */ + public boolean isDismissed() { + //TODO: recycling + return row == null || row.isDismissed(); + } + + public boolean isRowPinned() { + return row != null && row.isPinned(); + } + + public void setRowPinned(boolean pinned) { + if (row != null) row.setPinned(pinned); + } + + public boolean isRowAnimatingAway() { + return row != null && row.isHeadsUpAnimatingAway(); + } + + public boolean isRowHeadsUp() { + return row != null && row.isHeadsUp(); + } + + public void setHeadsUp(boolean shouldHeadsUp) { + if (row != null) row.setHeadsUp(shouldHeadsUp); + } + + public boolean mustStayOnScreen() { + return row != null && row.mustStayOnScreen(); + } + + public void setHeadsUpIsVisible() { + if (row != null) row.setHeadsUpIsVisible(); + } + + //TODO: i'm imagining a world where this isn't just the row, but I could be rwong + public ExpandableNotificationRow getHeadsUpAnimationView() { + return row; + } + + public void setUserLocked(boolean userLocked) { + if (row != null) row.setUserLocked(userLocked); + } + + public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) { + if (row != null) row.setUserExpanded(userExpanded, allowChildExpansion); + } + + public void setGroupExpansionChanging(boolean changing) { + if (row != null) row.setGroupExpansionChanging(changing); + } + + public void notifyHeightChanged(boolean needsAnimation) { + if (row != null) row.notifyHeightChanged(needsAnimation); + } + + public void closeRemoteInput() { + if (row != null) row.closeRemoteInput(); + } + + public boolean areChildrenExpanded() { + return row != null && row.areChildrenExpanded(); + } + + public boolean keepInParent() { + return row != null && row.keepInParent(); + } + + //TODO: probably less confusing to say "is group fully visible" + public boolean isGroupNotFullyVisible() { + return row == null || row.isGroupNotFullyVisible(); + } + + public NotificationGuts getGuts() { + if (row != null) return row.getGuts(); + return null; + } + + public boolean hasLowPriorityStateUpdated() { + return row != null && row.hasLowPriorityStateUpdated(); + } + + public void removeRow() { + if (row != null) row.setRemoved(); + } + + public boolean isSummaryWithChildren() { + return row != null && row.isSummaryWithChildren(); + } + + public void setKeepInParent(boolean keep) { + if (row != null) row.setKeepInParent(keep); + } + + public void onDensityOrFontScaleChanged() { + if (row != null) row.onDensityOrFontScaleChanged(); + } + + public boolean areGutsExposed() { + return row != null && row.getGuts() != null && row.getGuts().isExposed(); + } + + public boolean isChildInGroup() { + return parent == null; + } + + public void setLowPriorityStateUpdated(boolean updated) { + if (row != null) row.setLowPriorityStateUpdated(updated); + } + + /** + * @return Can the underlying notification be cleared? This can be different from whether the + * notification can be dismissed in case notifications are sensitive on the lockscreen. + * @see #canViewBeDismissed() + */ + public boolean isClearable() { + if (notification == null || !notification.isClearable()) { + return false; + } + if (children.size() > 0) { + for (int i = 0; i < children.size(); i++) { + NotificationEntry child = children.get(i); + if (!child.isClearable()) { + return false; + } + } + } + return true; + } + + public boolean canViewBeDismissed() { + if (row == null) return true; + return row.canViewBeDismissed(); + } + + @VisibleForTesting + boolean isExemptFromDndVisualSuppression() { + if (isNotificationBlockedByPolicy(notification.getNotification())) { + return false; + } + + if ((notification.getNotification().flags + & Notification.FLAG_FOREGROUND_SERVICE) != 0) { + return true; + } + if (notification.getNotification().isMediaNotification()) { + return true; + } + if (mIsSystemNotification != null && mIsSystemNotification) { + return true; + } + return false; + } + + private boolean shouldSuppressVisualEffect(int effect) { + if (isExemptFromDndVisualSuppression()) { + return false; + } + return (suppressedVisualEffects & effect) != 0; + } + + /** + * Returns whether {@link Policy#SUPPRESSED_EFFECT_FULL_SCREEN_INTENT} + * is set for this entry. + */ + public boolean shouldSuppressFullScreenIntent() { + return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT); + } + + /** + * Returns whether {@link Policy#SUPPRESSED_EFFECT_PEEK} + * is set for this entry. + */ + public boolean shouldSuppressPeek() { + return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_PEEK); + } + + /** + * Returns whether {@link Policy#SUPPRESSED_EFFECT_STATUS_BAR} + * is set for this entry. + */ + public boolean shouldSuppressStatusBar() { + return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_STATUS_BAR); + } + + /** + * Returns whether {@link Policy#SUPPRESSED_EFFECT_AMBIENT} + * is set for this entry. + */ + public boolean shouldSuppressAmbient() { + return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_AMBIENT); + } + + /** + * Returns whether {@link Policy#SUPPRESSED_EFFECT_NOTIFICATION_LIST} + * is set for this entry. + */ + public boolean shouldSuppressNotificationList() { + return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_NOTIFICATION_LIST); + } + + /** + * Categories that are explicitly called out on DND settings screens are always blocked, if + * DND has flagged them, even if they are foreground or system notifications that might + * otherwise visually bypass DND. + */ + private static boolean isNotificationBlockedByPolicy(Notification n) { + return isCategory(CATEGORY_CALL, n) + || isCategory(CATEGORY_MESSAGE, n) + || isCategory(CATEGORY_ALARM, n) + || isCategory(CATEGORY_EVENT, n) + || isCategory(CATEGORY_REMINDER, n); + } + + private static boolean isCategory(String category, Notification n) { + return Objects.equals(n.category, category); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java index 43048a2c087e..3eec38e14083 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java @@ -33,9 +33,9 @@ import com.android.systemui.UiOffloadThread; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarStateController.StateListener; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -115,11 +115,11 @@ public class NotificationLogger implements StateListener { // notifications. // 3. Report newly visible and no-longer visible notifications. // 4. Keep currently visible notifications for next report. - ArrayList<NotificationData.Entry> activeNotifications = mEntryManager + ArrayList<NotificationEntry> activeNotifications = mEntryManager .getNotificationData().getActiveNotifications(); int N = activeNotifications.size(); for (int i = 0; i < N; i++) { - NotificationData.Entry entry = activeNotifications.get(i); + NotificationEntry entry = activeNotifications.get(i); String key = entry.notification.getKey(); boolean isVisible = mListContainer.isInVisibleLocation(entry); NotificationVisibility visObj = NotificationVisibility.obtain(key, i, N, isVisible); @@ -167,7 +167,7 @@ public class NotificationLogger implements StateListener { entryManager.addNotificationEntryListener(new NotificationEntryListener() { @Override public void onEntryRemoved( - NotificationData.Entry entry, + NotificationEntry entry, NotificationVisibility visibility, boolean removedByUser) { if (removedByUser && visibility != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index a58c7cde32a7..9e0dd8461d21 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -86,10 +86,10 @@ import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.notification.AboveShelfChangedListener; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.logging.NotificationCounters; import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; @@ -199,7 +199,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private ExpansionLogger mLogger; private String mLoggingKey; private NotificationGuts mGuts; - private NotificationData.Entry mEntry; + private NotificationEntry mEntry; private StatusBarNotification mStatusBarNotification; private String mAppName; @@ -451,7 +451,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView * * @param entry the entry this row is tied to */ - public void setEntry(@NonNull NotificationData.Entry entry) { + public void setEntry(@NonNull NotificationEntry entry) { mEntry = entry; mStatusBarNotification = entry.notification; cacheIsSystemNotification(); @@ -685,7 +685,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return mStatusBarNotification; } - public NotificationData.Entry getEntry() { + public NotificationEntry getEntry() { return mEntry; } @@ -1422,7 +1422,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView public void performDismiss(boolean fromAccessibility) { if (isOnlyChildInGroup()) { - NotificationData.Entry groupSummary = + NotificationEntry groupSummary = mGroupManager.getLogicalGroupSummary(getStatusBarNotification()); if (groupSummary.isClearable()) { // If this is the only child in the group, dismiss the group, but don't try to show @@ -2538,7 +2538,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView /** * @return Whether this view is allowed to be dismissed. Only valid for visible notifications as * otherwise some state might not be updated. To request about the general clearability - * see {@link NotificationData.Entry#isClearable()}. + * see {@link NotificationEntry#isClearable()}. */ public boolean canViewBeDismissed() { return mEntry.isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral); @@ -2969,7 +2969,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } public interface OnExpandClickListener { - void onExpandClicked(NotificationData.Entry clickedEntry, boolean nowExpanded); + void onExpandClicked(NotificationEntry clickedEntry, boolean nowExpanded); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index 02a310c29379..bf30cf9a53c0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -46,8 +46,8 @@ import com.android.systemui.R; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.TransformableView; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationUtils; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.wrapper.NotificationCustomViewWrapper; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; import com.android.systemui.statusbar.phone.NotificationGroupManager; @@ -1231,7 +1231,7 @@ public class NotificationContentView extends FrameLayout { updateAllSingleLineViews(); } - public void onNotificationUpdated(NotificationData.Entry entry) { + public void onNotificationUpdated(NotificationEntry entry) { mStatusBarNotification = entry.notification; mOnContentViewInactiveListeners.clear(); mBeforeN = entry.targetSdk < Build.VERSION_CODES.N; @@ -1292,7 +1292,7 @@ public class NotificationContentView extends FrameLayout { } } - private void applyRemoteInputAndSmartReply(final NotificationData.Entry entry) { + private void applyRemoteInputAndSmartReply(final NotificationEntry entry) { if (mRemoteInputController == null) { return; } @@ -1313,7 +1313,7 @@ public class NotificationContentView extends FrameLayout { @VisibleForTesting static SmartRepliesAndActions chooseSmartRepliesAndActions( SmartReplyConstants smartReplyConstants, - final NotificationData.Entry entry) { + final NotificationEntry entry) { boolean enableAppGeneratedSmartReplies = (smartReplyConstants.isEnabled() && (!smartReplyConstants.requiresTargetingP() || entry.targetSdk >= Build.VERSION_CODES.P)); @@ -1370,7 +1370,7 @@ public class NotificationContentView extends FrameLayout { smartReplies, smartActions, freeformRemoteInputActionPair != null); } - private void applyRemoteInput(NotificationData.Entry entry, boolean hasFreeformRemoteInput) { + private void applyRemoteInput(NotificationEntry entry, boolean hasFreeformRemoteInput) { View bigContentView = mExpandedChild; if (bigContentView != null) { mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasFreeformRemoteInput, @@ -1402,7 +1402,7 @@ public class NotificationContentView extends FrameLayout { mCachedHeadsUpRemoteInput = null; } - private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry, + private RemoteInputView applyRemoteInput(View view, NotificationEntry entry, boolean hasRemoteInput, PendingIntent existingPendingIntent, RemoteInputView cachedView, NotificationViewWrapper wrapper) { View actionContainerCandidate = view.findViewById( @@ -1470,7 +1470,7 @@ public class NotificationContentView extends FrameLayout { } private void applySmartReplyView(SmartRepliesAndActions smartRepliesAndActions, - NotificationData.Entry entry) { + NotificationEntry entry) { if (mExpandedChild != null) { mExpandedSmartReplyView = applySmartReplyView(mExpandedChild, smartRepliesAndActions, entry); @@ -1496,7 +1496,7 @@ public class NotificationContentView extends FrameLayout { } private SmartReplyView applySmartReplyView(View view, - SmartRepliesAndActions smartRepliesAndActions, NotificationData.Entry entry) { + SmartRepliesAndActions smartRepliesAndActions, NotificationEntry entry) { View smartReplyContainerCandidate = view.findViewById( com.android.internal.R.id.smart_reply_container); if (!(smartReplyContainerCandidate instanceof LinearLayout)) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index ac4e5830d76b..bd1dfb181afe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -48,7 +48,7 @@ import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.StatusBar; @@ -116,7 +116,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mNotificationActivityStarter = notificationActivityStarter; } - public void onDensityOrFontScaleChanged(NotificationData.Entry entry) { + public void onDensityOrFontScaleChanged(NotificationEntry entry) { setExposedGuts(entry.getGuts()); bindGuts(entry.getRow()); } @@ -429,7 +429,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx } @Override - public boolean shouldExtendLifetime(NotificationData.Entry entry) { + public boolean shouldExtendLifetime(NotificationEntry entry) { return entry != null &&(mNotificationGutsExposed != null && entry.getGuts() != null @@ -438,7 +438,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx } @Override - public void setShouldManageLifetime(NotificationData.Entry entry, boolean shouldExtend) { + public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) { if (shouldExtend) { mKeyToRemoveOnGutsClosed = entry.key; if (Log.isLoggable(TAG, Log.DEBUG)) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java index 9908049984d1..42ebfceca334 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java @@ -37,7 +37,7 @@ import com.android.internal.widget.ImageMessageConsumer; import com.android.systemui.statusbar.InflationTask; import com.android.systemui.statusbar.notification.InflationException; import com.android.systemui.statusbar.notification.MediaNotificationProcessor; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.util.Assert; @@ -614,7 +614,7 @@ public class NotificationInflater { @Nullable InflationCallback endListener, ExpandableNotificationRow row, boolean redactAmbient) { Assert.isMainThread(); - NotificationData.Entry entry = row.getEntry(); + NotificationEntry entry = row.getEntry(); NotificationContentView privateLayout = row.getPrivateLayout(); NotificationContentView publicLayout = row.getPublicLayout(); if (runningInflations.isEmpty()) { @@ -724,7 +724,7 @@ public class NotificationInflater { * @param entry the entry with the content views set * @param inflatedFlags the flags associated with the content views that were inflated */ - void onAsyncInflationFinished(NotificationData.Entry entry, + void onAsyncInflationFinished(NotificationEntry entry, @InflationFlag int inflatedFlags); /** @@ -782,7 +782,7 @@ public class NotificationInflater { mRedactAmbient = redactAmbient; mRemoteViewClickHandler = remoteViewClickHandler; mCallback = callback; - NotificationData.Entry entry = row.getEntry(); + NotificationEntry entry = row.getEntry(); entry.setInflationTask(this); } @@ -857,7 +857,7 @@ public class NotificationInflater { } @Override - public void onAsyncInflationFinished(NotificationData.Entry entry, + public void onAsyncInflationFinished(NotificationEntry entry, @InflationFlag int inflatedFlags) { mRow.getEntry().onInflationTaskFinished(); mRow.onNotificationUpdated(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java index 1741a0b6b37c..0160c547b336 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java @@ -25,7 +25,7 @@ import androidx.asynclayoutinflater.view.AsyncLayoutInflater; import com.android.systemui.R; import com.android.systemui.statusbar.InflationTask; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; /** * An inflater task that asynchronously inflates a ExpandableNotificationRow @@ -36,14 +36,14 @@ public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInf private static final boolean TRACE_ORIGIN = true; private RowInflationFinishedListener mListener; - private NotificationData.Entry mEntry; + private NotificationEntry mEntry; private boolean mCancelled; private Throwable mInflateOrigin; /** * Inflates a new notificationView. This should not be called twice on this object */ - public void inflate(Context context, ViewGroup parent, NotificationData.Entry entry, + public void inflate(Context context, ViewGroup parent, NotificationEntry entry, RowInflationFinishedListener listener) { if (TRACE_ORIGIN) { mInflateOrigin = new Throwable("inflate requested here"); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java index 670908fe175d..cbec37effe18 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java @@ -25,7 +25,7 @@ import com.android.systemui.R; import com.android.systemui.statusbar.AmbientPulseManager; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; @@ -358,7 +358,7 @@ public class AmbientState { mPulsing = hasPulsing; } - public boolean isPulsing(NotificationData.Entry entry) { + public boolean isPulsing(NotificationEntry entry) { if (!mPulsing || mAmbientPulseManager == null) { return false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java index 1d5b9cce099d..f771be043c07 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java @@ -22,8 +22,8 @@ import android.view.View; import android.view.ViewGroup; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.VisibilityLocationProvider; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; @@ -117,7 +117,7 @@ public interface NotificationListContainer extends ExpandableView.OnHeightChange * @param entry entry to get the view parent for * @return the view parent for entry */ - ViewGroup getViewParentForNotification(NotificationData.Entry entry); + ViewGroup getViewParentForNotification(NotificationEntry entry); /** * Resets the currently exposed menu view. @@ -140,7 +140,7 @@ public interface NotificationListContainer extends ExpandableView.OnHeightChange * * @param entry the entry whose view's view state needs to be cleaned up (say that 5 times fast) */ - void cleanUpViewStateForEntry(NotificationData.Entry entry); + void cleanUpViewStateForEntry(NotificationEntry entry); /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java index 4f0831f1043c..941860177d1b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java @@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification.stack; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.NUM_SECTIONS; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; @@ -49,12 +49,12 @@ class NotificationRoundnessManager implements OnHeadsUpChangedListener { } @Override - public void onHeadsUpPinned(NotificationData.Entry headsUp) { + public void onHeadsUpPinned(NotificationEntry headsUp) { updateView(headsUp.getRow(), false /* animate */); } @Override - public void onHeadsUpUnPinned(NotificationData.Entry headsUp) { + public void onHeadsUpUnPinned(NotificationEntry headsUp) { updateView(headsUp.getRow(), true /* animate */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 1248cbff5e1a..e4b00dd6d38c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -96,13 +96,13 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.notification.FakeShadowView; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.ShadeViewRefactor; import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent; import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -521,7 +521,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mEntryManager.addNotificationEntryListener(new NotificationEntryListener() { @Override - public void onEntryUpdated(NotificationData.Entry entry) { + public void onEntryUpdated(NotificationEntry entry) { if (!entry.notification.isClearable()) { // The user may have performed a dismiss action on the notification, since it's // not clearable we should snap it back. @@ -602,14 +602,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public RemoteInputController.Delegate createDelegate() { return new RemoteInputController.Delegate() { - public void setRemoteInputActive(NotificationData.Entry entry, + public void setRemoteInputActive(NotificationEntry entry, boolean remoteInputActive) { mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive); entry.notifyHeightChanged(true /* needsAnimation */); updateFooter(); } - public void lockScrollTo(NotificationData.Entry entry) { + public void lockScrollTo(NotificationEntry entry) { NotificationStackScrollLayout.this.lockScrollTo(entry.getRow()); } @@ -922,7 +922,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @Override @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM) - public boolean isInVisibleLocation(NotificationData.Entry entry) { + public boolean isInVisibleLocation(NotificationEntry entry) { ExpandableNotificationRow row = entry.getRow(); ExpandableViewState childViewState = row.getViewState(); @@ -1236,13 +1236,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd */ @ShadeViewRefactor(RefactorComponent.COORDINATOR) private int getTopHeadsUpPinnedHeight() { - NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry(); + NotificationEntry topEntry = mHeadsUpManager.getTopEntry(); if (topEntry == null) { return 0; } ExpandableNotificationRow row = topEntry.getRow(); if (row.isChildInGroup()) { - final NotificationData.Entry groupSummary + final NotificationEntry groupSummary = mGroupManager.getGroupSummary(row.getStatusBarNotification()); if (groupSummary != null) { row = groupSummary.getRow(); @@ -1417,7 +1417,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) { if (slidingChild instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild; - NotificationData.Entry entry = row.getEntry(); + NotificationEntry entry = row.getEntry(); if (!mIsExpanded && row.isHeadsUp() && row.isPinned() && mHeadsUpManager.getTopEntry().getRow() != row && mGroupManager.getGroupSummary( @@ -1551,7 +1551,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - private void snapViewIfNeeded(NotificationData.Entry entry) { + private void snapViewIfNeeded(NotificationEntry entry) { ExpandableNotificationRow child = entry.getRow(); boolean animate = mIsExpanded || isPinnedHeadsUp(child); // If the child is showing the notification menu snap to that @@ -1561,7 +1561,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @Override @ShadeViewRefactor(RefactorComponent.ADAPTER) - public ViewGroup getViewParentForNotification(NotificationData.Entry entry) { + public ViewGroup getViewParentForNotification(NotificationEntry entry) { return this; } @@ -2064,7 +2064,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - private boolean isPulsing(NotificationData.Entry entry) { + private boolean isPulsing(NotificationEntry entry) { return mAmbientState.isPulsing(entry); } @@ -2569,7 +2569,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) @Override - public void cleanUpViewStateForEntry(NotificationData.Entry entry) { + public void cleanUpViewStateForEntry(NotificationEntry entry) { View child = entry.getRow(); if (child == mSwipeHelper.getTranslatingParentView()) { mSwipeHelper.clearTranslatingParentView(); @@ -2697,7 +2697,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private boolean isChildInInvisibleGroup(View child) { if (child instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) child; - NotificationData.Entry groupSummary = + NotificationEntry groupSummary = mGroupManager.getGroupSummary(row.getStatusBarNotification()); if (groupSummary != null && groupSummary.getRow() != row) { return row.getVisibility() == View.INVISIBLE; @@ -4716,7 +4716,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed); } - public void generateHeadsUpAnimation(NotificationData.Entry entry, boolean isHeadsUp) { + public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) { ExpandableNotificationRow row = entry.getHeadsUpAnimationView(); generateHeadsUpAnimation(row, isHeadsUp); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java index d1e488ab2b4d..876b9028a9b6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -28,7 +28,7 @@ import com.android.systemui.R; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.HeadsUpStatusBarView; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; @@ -143,7 +143,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, } @Override - public void onHeadsUpPinned(NotificationData.Entry entry) { + public void onHeadsUpPinned(NotificationEntry entry) { updateTopEntry(); updateHeader(entry); } @@ -206,11 +206,11 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, } private void updateTopEntry() { - NotificationData.Entry newEntry = null; + NotificationEntry newEntry = null; if (!mIsExpanded && mHeadsUpManager.hasPinnedHeadsUp()) { newEntry = mHeadsUpManager.getTopEntry(); } - NotificationData.Entry previousEntry = mHeadsUpStatusBarView.getShowingEntry(); + NotificationEntry previousEntry = mHeadsUpStatusBarView.getShowingEntry(); mHeadsUpStatusBarView.setEntry(newEntry); if (newEntry != previousEntry) { boolean animateIsolation = false; @@ -298,7 +298,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, } @Override - public void onHeadsUpUnPinned(NotificationData.Entry entry) { + public void onHeadsUpUnPinned(NotificationEntry entry) { updateTopEntry(); updateHeader(entry); } @@ -338,7 +338,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, }); } - public void updateHeader(NotificationData.Entry entry) { + public void updateHeader(NotificationEntry entry) { ExpandableNotificationRow row = entry.getRow(); float headerVisibleAmount = 1.0f; if (row.isPinned() || row.isHeadsUpAnimatingAway() || row == mTrackedChild) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index f4cfd4197637..0fada667dd2f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -41,8 +41,8 @@ import com.android.systemui.bubbles.BubbleController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarStateController.StateListener; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -72,8 +72,8 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, private int mDisplayCutoutTouchableRegionSize; private boolean mTrackingHeadsUp; private HashSet<String> mSwipedOutKeys = new HashSet<>(); - private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>(); - private ArraySet<NotificationData.Entry> mEntriesToRemoveWhenReorderingAllowed + private HashSet<NotificationEntry> mEntriesToRemoveAfterExpand = new HashSet<>(); + private ArraySet<NotificationEntry> mEntriesToRemoveWhenReorderingAllowed = new ArraySet<>(); private boolean mIsExpanded; private int[] mTmpTwoArray = new int[2]; @@ -187,7 +187,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, releaseAllImmediately(); mReleaseOnExpandFinish = false; } else { - for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) { + for (NotificationEntry entry : mEntriesToRemoveAfterExpand) { if (isAlerting(entry.key)) { // Maybe the heads-up was removed already removeAlertEntry(entry.key); @@ -252,7 +252,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, * @param remoteInputActive True to notify active, False to notify inactive. */ public void setRemoteInputActive( - @NonNull NotificationData.Entry entry, boolean remoteInputActive) { + @NonNull NotificationEntry entry, boolean remoteInputActive) { HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.key); if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) { headsUpEntry.remoteInputActive = remoteInputActive; @@ -268,7 +268,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, * Sets whether an entry's menu row is exposed and therefore it should stick in the heads up * area if it's pinned until it's hidden again. */ - public void setMenuShown(@NonNull NotificationData.Entry entry, boolean menuShown) { + public void setMenuShown(@NonNull NotificationEntry entry, boolean menuShown) { HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key); if (headsUpEntry instanceof HeadsUpEntryPhone && entry.isRowPinned()) { ((HeadsUpEntryPhone) headsUpEntry).setMenuShownPinned(menuShown); @@ -315,9 +315,9 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, return; } if (hasPinnedHeadsUp()) { - NotificationData.Entry topEntry = getTopEntry(); + NotificationEntry topEntry = getTopEntry(); if (topEntry.isChildInGroup()) { - final NotificationData.Entry groupSummary + final NotificationEntry groupSummary = mGroupManager.getGroupSummary(topEntry.notification); if (groupSummary != null) { topEntry = groupSummary; @@ -374,7 +374,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, @Override public void onReorderingAllowed() { mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false); - for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) { + for (NotificationEntry entry : mEntriesToRemoveWhenReorderingAllowed) { if (isAlerting(entry.key)) { // Maybe the heads-up was removed already removeAlertEntry(entry.key); @@ -399,7 +399,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, } @Override - protected boolean shouldHeadsUpBecomePinned(NotificationData.Entry entry) { + protected boolean shouldHeadsUpBecomePinned(NotificationEntry entry) { return mStatusBarState != StatusBarState.KEYGUARD && !mIsExpanded || super.shouldHeadsUpBecomePinned(entry); } @@ -488,7 +488,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, return super.isSticky() || mMenuShownPinned; } - public void setEntry(@NonNull final NotificationData.Entry entry) { + public void setEntry(@NonNull final NotificationEntry entry) { Runnable removeHeadsUpRunnable = () -> { if (!mVisualStabilityManager.isReorderingAllowed()) { mEntriesToRemoveWhenReorderingAllowed.add(entry); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java index 9c1c71a2dec8..dd200da56d20 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java @@ -21,7 +21,7 @@ import android.view.MotionEvent; import android.view.ViewConfiguration; import com.android.systemui.Gefingerpoken; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; @@ -83,7 +83,7 @@ public class HeadsUpTouchHelper implements Gefingerpoken { } else if (child == null && !mCallback.isExpanded()) { // We might touch above the visible heads up child, but then we still would // like to capture it. - NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry(); + NotificationEntry topEntry = mHeadsUpManager.getTopEntry(); if (topEntry != null && topEntry.isRowPinned()) { mPickedChild = topEntry.getRow(); mTouchingHeadsUpView = true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java index 927228eae20e..925a19d6f5eb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java @@ -22,7 +22,7 @@ import android.util.Log; import com.android.systemui.Dependency; import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment; +import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment; import com.android.systemui.statusbar.policy.DeviceProvisionedController; public class KeyguardEnvironmentImpl implements KeyguardEnvironment { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java index af3257ae423e..d364356b19fe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java @@ -31,9 +31,9 @@ import com.android.systemui.statusbar.AmbientPulseManager.OnAmbientChangedListen import com.android.systemui.statusbar.InflationTask; import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarStateController.StateListener; -import com.android.systemui.statusbar.notification.NotificationData.Entry; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationInflater.AsyncInflationTask; import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag; import com.android.systemui.statusbar.phone.NotificationGroupManager.NotificationGroup; @@ -108,7 +108,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis * @param entry notification to check * @return true if the entry was transferred to and should inflate + alert */ - public boolean isAlertTransferPending(@NonNull Entry entry) { + public boolean isAlertTransferPending(@NonNull NotificationEntry entry) { PendingAlertInfo alertInfo = mPendingAlerts.get(entry.key); return alertInfo != null && alertInfo.isStillValid(); } @@ -172,16 +172,16 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis }; @Override - public void onAmbientStateChanged(Entry entry, boolean isAmbient) { + public void onAmbientStateChanged(NotificationEntry entry, boolean isAmbient) { onAlertStateChanged(entry, isAmbient, mAmbientPulseManager); } @Override - public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) { + public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { onAlertStateChanged(entry, isHeadsUp, mHeadsUpManager); } - private void onAlertStateChanged(Entry entry, boolean isAlerting, + private void onAlertStateChanged(NotificationEntry entry, boolean isAlerting, AlertingNotificationManager alertManager) { if (isAlerting && mGroupManager.isSummaryOfSuppressedGroup(entry.notification)) { handleSuppressedSummaryAlerted(entry, alertManager); @@ -193,7 +193,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis // Called when a new notification has been posted but is not inflated yet. We use this to // see as early as we can if we need to abort a transfer. @Override - public void onPendingEntryAdded(Entry entry) { + public void onPendingEntryAdded(NotificationEntry entry) { String groupKey = mGroupManager.getGroupKey(entry.notification); GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(groupKey); if (groupAlertEntry != null) { @@ -204,7 +204,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis // Called when the entry's reinflation has finished. If there is an alert pending, we // then show the alert. @Override - public void onEntryReinflated(Entry entry) { + public void onEntryReinflated(NotificationEntry entry) { PendingAlertInfo alertInfo = mPendingAlerts.remove(entry.key); if (alertInfo != null) { if (alertInfo.isStillValid()) { @@ -219,7 +219,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis @Override public void onEntryRemoved( - @Nullable Entry entry, + @Nullable NotificationEntry entry, NotificationVisibility visibility, boolean removedByUser) { // Removes any alerts pending on this entry. Note that this will not stop any inflation @@ -241,8 +241,8 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis return 0; } int number = 0; - Iterable<Entry> values = mEntryManager.getPendingNotificationsIterator(); - for (Entry entry : values) { + Iterable<NotificationEntry> values = mEntryManager.getPendingNotificationsIterator(); + for (NotificationEntry entry : values) { if (isPendingNotificationInGroup(entry, group) && onlySummaryAlerts(entry)) { number++; } @@ -260,8 +260,8 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis if (mEntryManager == null) { return false; } - Iterable<Entry> values = mEntryManager.getPendingNotificationsIterator(); - for (Entry entry : values) { + Iterable<NotificationEntry> values = mEntryManager.getPendingNotificationsIterator(); + for (NotificationEntry entry : values) { if (isPendingNotificationInGroup(entry, group)) { return true; } @@ -276,7 +276,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis * @param group group to check * @return true if the notification will add to the group, false o/w */ - private boolean isPendingNotificationInGroup(@NonNull Entry entry, + private boolean isPendingNotificationInGroup(@NonNull NotificationEntry entry, @NonNull NotificationGroup group) { String groupKey = mGroupManager.getGroupKey(group.summary.notification); return mGroupManager.isGroupChild(entry.notification) @@ -293,7 +293,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis * @param summary the summary that is suppressed and alerting * @param alertManager the alert manager that manages the alerting summary */ - private void handleSuppressedSummaryAlerted(@NonNull Entry summary, + private void handleSuppressedSummaryAlerted(@NonNull NotificationEntry summary, @NonNull AlertingNotificationManager alertManager) { StatusBarNotification sbn = summary.notification; GroupAlertEntry groupAlertEntry = @@ -309,7 +309,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis return; } - Entry child = mGroupManager.getLogicalChildren(summary.notification).iterator().next(); + NotificationEntry child = mGroupManager.getLogicalChildren(summary.notification).iterator().next(); if (child != null) { if (child.getRow().keepInParent() || child.isRowRemoved() @@ -333,7 +333,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis * @param toEntry entry to transfer to * @param alertManager alert manager for the alert type */ - private void transferAlertState(@NonNull Entry fromEntry, @NonNull Entry toEntry, + private void transferAlertState(@NonNull NotificationEntry fromEntry, @NonNull NotificationEntry toEntry, @NonNull AlertingNotificationManager alertManager) { alertManager.removeNotification(fromEntry.key, true /* releaseImmediately */); alertNotificationWhenPossible(toEntry, alertManager); @@ -353,13 +353,13 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis private void checkShouldTransferBack(@NonNull GroupAlertEntry groupAlertEntry) { if (SystemClock.elapsedRealtime() - groupAlertEntry.mLastAlertTransferTime < ALERT_TRANSFER_TIMEOUT) { - Entry summary = groupAlertEntry.mGroup.summary; + NotificationEntry summary = groupAlertEntry.mGroup.summary; AlertingNotificationManager alertManager = getActiveAlertManager(); if (!onlySummaryAlerts(summary)) { return; } - ArrayList<Entry> children = mGroupManager.getLogicalChildren(summary.notification); + ArrayList<NotificationEntry> children = mGroupManager.getLogicalChildren(summary.notification); int numChildren = children.size(); int numPendingChildren = getPendingChildrenNotAlerting(groupAlertEntry.mGroup); numChildren += numPendingChildren; @@ -368,7 +368,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis } boolean releasedChild = false; for (int i = 0; i < children.size(); i++) { - Entry entry = children.get(i); + NotificationEntry entry = children.get(i); if (onlySummaryAlerts(entry) && alertManager.isAlerting(entry.key)) { releasedChild = true; alertManager.removeNotification(entry.key, true /* releaseImmediately */); @@ -399,7 +399,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis * @param entry entry to show * @param alertManager alert manager for the alert type */ - private void alertNotificationWhenPossible(@NonNull Entry entry, + private void alertNotificationWhenPossible(@NonNull NotificationEntry entry, @NonNull AlertingNotificationManager alertManager) { @InflationFlag int contentFlag = alertManager.getContentFlag(); if (!entry.getRow().isInflationFlagSet(contentFlag)) { @@ -419,7 +419,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis return mIsDozing ? mAmbientPulseManager : mHeadsUpManager; } - private boolean onlySummaryAlerts(Entry entry) { + private boolean onlySummaryAlerts(NotificationEntry entry) { return entry.notification.getNotification().getGroupAlertBehavior() == Notification.GROUP_ALERT_SUMMARY; } @@ -439,7 +439,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis * the transfer is still valid if the notification is updated. */ final StatusBarNotification mOriginalNotification; - final Entry mEntry; + final NotificationEntry mEntry; /** * The notification is still pending inflation but we've decided that we no longer need @@ -450,7 +450,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis */ boolean mAbortOnInflation; - PendingAlertInfo(Entry entry, AlertingNotificationManager alertManager) { + PendingAlertInfo(NotificationEntry entry, AlertingNotificationManager alertManager) { mOriginalNotification = entry.notification; mEntry = entry; mAlertManager = alertManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java index 3c1c0765a3b8..bb9e4183ba76 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java @@ -27,7 +27,7 @@ import com.android.systemui.statusbar.AmbientPulseManager.OnAmbientChangedListen import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarStateController.StateListener; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; @@ -97,7 +97,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, } } - public void onEntryRemoved(NotificationData.Entry removed) { + public void onEntryRemoved(NotificationEntry removed) { onEntryRemovedInternal(removed, removed.notification); mIsolatedEntries.remove(removed.key); } @@ -109,7 +109,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, * @param sbn the notification the entry has, which doesn't need to be the same as it's internal * notification */ - private void onEntryRemovedInternal(NotificationData.Entry removed, + private void onEntryRemovedInternal(NotificationEntry removed, final StatusBarNotification sbn) { String groupKey = getGroupKey(sbn); final NotificationGroup group = mGroupMap.get(groupKey); @@ -136,7 +136,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, } } - public void onEntryAdded(final NotificationData.Entry added) { + public void onEntryAdded(final NotificationEntry added) { if (added.isRowRemoved()) { added.setDebugThrowable(new Throwable()); } @@ -152,7 +152,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, } } if (isGroupChild) { - NotificationData.Entry existing = group.children.get(added.key); + NotificationEntry existing = group.children.get(added.key); if (existing != null && existing != added) { Throwable existingThrowable = existing.getDebugThrowable(); Log.wtf(TAG, "Inconsistent entries found with the same key " + added.key @@ -169,9 +169,9 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, group.expanded = added.areChildrenExpanded(); updateSuppression(group); if (!group.children.isEmpty()) { - ArrayList<NotificationData.Entry> childrenCopy + ArrayList<NotificationEntry> childrenCopy = new ArrayList<>(group.children.values()); - for (NotificationData.Entry child : childrenCopy) { + for (NotificationEntry child : childrenCopy) { onEntryBecomingChild(child); } for (OnGroupChangeListener listener : mListeners) { @@ -181,7 +181,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, } } - private void onEntryBecomingChild(NotificationData.Entry entry) { + private void onEntryBecomingChild(NotificationEntry entry) { if (shouldIsolate(entry)) { isolateNotification(entry); } @@ -221,7 +221,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, return count; } - private NotificationData.Entry getIsolatedChild(String groupKey) { + private NotificationEntry getIsolatedChild(String groupKey) { for (StatusBarNotification sbn : mIsolatedEntries.values()) { if (sbn.getGroupKey().equals(groupKey) && isIsolated(sbn)) { return mGroupMap.get(sbn.getKey()).summary; @@ -230,7 +230,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, return null; } - public void onEntryUpdated(NotificationData.Entry entry, + public void onEntryUpdated(NotificationEntry entry, StatusBarNotification oldNotification) { String oldKey = oldNotification.getGroupKey(); String newKey = entry.notification.getGroupKey(); @@ -267,7 +267,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, if (!isOnlyChild(sbn)) { return false; } - NotificationData.Entry logicalGroupSummary = getLogicalGroupSummary(sbn); + NotificationEntry logicalGroupSummary = getLogicalGroupSummary(sbn); return logicalGroupSummary != null && !logicalGroupSummary.notification.equals(sbn); } @@ -343,7 +343,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, * Get the summary of a specified status bar notification. For isolated notification this return * itself. */ - public NotificationData.Entry getGroupSummary(StatusBarNotification sbn) { + public NotificationEntry getGroupSummary(StatusBarNotification sbn) { return getGroupSummary(getGroupKey(sbn)); } @@ -352,12 +352,12 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, * but the logical summary, i.e when a child is isolated, it still returns the summary as if * it wasn't isolated. */ - public NotificationData.Entry getLogicalGroupSummary(StatusBarNotification sbn) { + public NotificationEntry getLogicalGroupSummary(StatusBarNotification sbn) { return getGroupSummary(sbn.getGroupKey()); } @Nullable - private NotificationData.Entry getGroupSummary(String groupKey) { + private NotificationEntry getGroupSummary(String groupKey) { NotificationGroup group = mGroupMap.get(groupKey); //TODO: see if this can become an Entry return group == null ? null @@ -371,13 +371,13 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, * @param summary summary of a group * @return list of the children */ - public ArrayList<NotificationData.Entry> getLogicalChildren(StatusBarNotification summary) { + public ArrayList<NotificationEntry> getLogicalChildren(StatusBarNotification summary) { NotificationGroup group = mGroupMap.get(summary.getGroupKey()); if (group == null) { return null; } - ArrayList<NotificationData.Entry> children = new ArrayList<>(group.children.values()); - NotificationData.Entry isolatedChild = getIsolatedChild(summary.getGroupKey()); + ArrayList<NotificationEntry> children = new ArrayList<>(group.children.values()); + NotificationEntry isolatedChild = getIsolatedChild(summary.getGroupKey()); if (isolatedChild != null) { children.add(isolatedChild); } @@ -443,24 +443,24 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, } @Override - public void onHeadsUpPinned(NotificationData.Entry entry) { + public void onHeadsUpPinned(NotificationEntry entry) { } @Override - public void onHeadsUpUnPinned(NotificationData.Entry entry) { + public void onHeadsUpUnPinned(NotificationEntry entry) { } @Override - public void onAmbientStateChanged(NotificationData.Entry entry, boolean isAmbient) { + public void onAmbientStateChanged(NotificationEntry entry, boolean isAmbient) { onAlertStateChanged(entry, isAmbient); } @Override - public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) { + public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { onAlertStateChanged(entry, isHeadsUp); } - private void onAlertStateChanged(NotificationData.Entry entry, boolean isAlerting) { + private void onAlertStateChanged(NotificationEntry entry, boolean isAlerting) { if (isAlerting) { if (shouldIsolate(entry)) { isolateNotification(entry); @@ -479,7 +479,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, * @return true if the entry should be isolated */ - private boolean shouldIsolate(NotificationData.Entry entry) { + private boolean shouldIsolate(NotificationEntry entry) { StatusBarNotification sbn = entry.notification; NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey()); if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) { @@ -499,7 +499,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, * * @param entry the notification to isolate */ - private void isolateNotification(NotificationData.Entry entry) { + private void isolateNotification(NotificationEntry entry) { StatusBarNotification sbn = entry.notification; // We will be isolated now, so lets update the groups @@ -523,7 +523,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, * * @param entry the notification to un-isolate */ - private void stopIsolatingNotification(NotificationData.Entry entry) { + private void stopIsolatingNotification(NotificationEntry entry) { StatusBarNotification sbn = entry.notification; if (mIsolatedEntries.containsKey(sbn.getKey())) { // not isolated anymore, we need to update the groups @@ -564,8 +564,8 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, } public static class NotificationGroup { - public final HashMap<String, NotificationData.Entry> children = new HashMap<>(); - public NotificationData.Entry summary; + public final HashMap<String, NotificationEntry> children = new HashMap<>(); + public NotificationEntry summary; public boolean expanded; /** * Is this notification group suppressed, i.e its summary is hidden @@ -580,7 +580,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, ? Log.getStackTraceString(summary.getDebugThrowable()) : ""); result += "\n children size: " + children.size(); - for (NotificationData.Entry child : children.values()) { + for (NotificationEntry child : children.values()) { result += "\n " + child.notification + (child.getDebugThrowable() != null ? Log.getStackTraceString(child.getDebugThrowable()) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index 056c8a7f5f5e..1fb5484196ae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -23,9 +23,9 @@ import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.StatusBarIconView; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationUtils; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.tuner.TunerService; @@ -182,7 +182,7 @@ public class NotificationIconAreaController implements DarkReceiver { return mStatusBar.getStatusBarHeight(); } - protected boolean shouldShowNotificationIcon(NotificationData.Entry entry, + protected boolean shouldShowNotificationIcon(NotificationEntry entry, boolean showAmbient, boolean showLowPriority, boolean hideDismissed, boolean hideRepliedMessages) { if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) { @@ -247,7 +247,7 @@ public class NotificationIconAreaController implements DarkReceiver { * @param hideDismissed should dismissed icons be hidden * @param hideRepliedMessages should messages that have been replied to be hidden */ - private void updateIconsForLayout(Function<NotificationData.Entry, StatusBarIconView> function, + private void updateIconsForLayout(Function<NotificationEntry, StatusBarIconView> function, NotificationIconContainer hostLayout, boolean showAmbient, boolean showLowPriority, boolean hideDismissed, boolean hideRepliedMessages) { ArrayList<StatusBarIconView> toShow = new ArrayList<>( @@ -257,7 +257,7 @@ public class NotificationIconAreaController implements DarkReceiver { for (int i = 0; i < mNotificationScrollLayout.getChildCount(); i++) { View view = mNotificationScrollLayout.getChildAt(i); if (view instanceof ExpandableNotificationRow) { - NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry(); + NotificationEntry ent = ((ExpandableNotificationRow) view).getEntry(); if (shouldShowNotificationIcon(ent, showAmbient, showLowPriority, hideDismissed, hideRepliedMessages)) { toShow.add(function.apply(ent)); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 16576ad6e3e1..d873b0cd1f26 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -76,9 +76,9 @@ import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.AnimatableProperty; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.PropertyAnimator; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; @@ -2489,12 +2489,12 @@ public class NotificationPanelView extends PanelView implements } @Override - public void onHeadsUpPinned(NotificationData.Entry entry) { + public void onHeadsUpPinned(NotificationEntry entry) { mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(), true); } @Override - public void onHeadsUpUnPinned(NotificationData.Entry entry) { + public void onHeadsUpUnPinned(NotificationEntry entry) { // When we're unpinning the notification via active edge they remain heads-upped, // we need to make sure that an animation happens in this case, otherwise the notification @@ -2507,7 +2507,7 @@ public class NotificationPanelView extends PanelView implements } @Override - public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) { + public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { mNotificationStackScroller.generateHeadsUpAnimation(entry, isHeadsUp); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 977e33688831..6d78f7204163 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -192,12 +192,11 @@ import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationAlertingManager; import com.android.systemui.statusbar.notification.NotificationClicker; -import com.android.systemui.statusbar.notification.NotificationData; -import com.android.systemui.statusbar.notification.NotificationData.Entry; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.NotificationRowBinder; import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -489,7 +488,7 @@ public class StatusBar extends SystemUI implements DemoMode, private Runnable mLaunchTransitionEndRunnable; protected boolean mLaunchTransitionFadingAway; - private NotificationData.Entry mDraggedDownEntry; + private NotificationEntry mDraggedDownEntry; private boolean mLaunchCameraOnScreenTurningOn; private boolean mLaunchCameraOnFinishedGoingToSleep; private int mLastCameraLaunchSource; @@ -633,7 +632,7 @@ public class StatusBar extends SystemUI implements DemoMode, mBubbleController.setExpandListener(mBubbleExpandListener); KeyguardSliceProvider sliceProvider = KeyguardSliceProvider.getAttachedInstance(); if (sliceProvider != null) { - sliceProvider.initDependencies(); + sliceProvider.initDependencies(mMediaManager, mStatusBarStateController); } else { Log.w(TAG, "Cannot init KeyguardSliceProvider dependencies"); } @@ -1510,21 +1509,21 @@ public class StatusBar extends SystemUI implements DemoMode, } @Override - public void onHeadsUpPinned(NotificationData.Entry entry) { + public void onHeadsUpPinned(NotificationEntry entry) { dismissVolumeDialog(); } @Override - public void onHeadsUpUnPinned(NotificationData.Entry entry) { + public void onHeadsUpUnPinned(NotificationEntry entry) { } @Override - public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) { + public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { mEntryManager.updateNotificationRanking(null /* rankingMap */); } @Override - public void onAmbientStateChanged(Entry entry, boolean isAmbient) { + public void onAmbientStateChanged(NotificationEntry entry, boolean isAmbient) { mEntryManager.updateNotificationRanking(null); if (isAmbient) { mDozeServiceHost.fireNotificationPulse(); @@ -2551,11 +2550,11 @@ public class StatusBar extends SystemUI implements DemoMode, }; public void resetUserExpandedStates() { - ArrayList<Entry> activeNotifications = mEntryManager.getNotificationData() + ArrayList<NotificationEntry> activeNotifications = mEntryManager.getNotificationData() .getActiveNotifications(); final int notificationCount = activeNotifications.size(); for (int i = 0; i < notificationCount; i++) { - NotificationData.Entry entry = activeNotifications.get(i); + NotificationEntry entry = activeNotifications.get(i); entry.resetUserExpansion(); } } @@ -3506,7 +3505,7 @@ public class StatusBar extends SystemUI implements DemoMode, int userId = mLockscreenUserManager.getCurrentUserId(); ExpandableNotificationRow row = null; - NotificationData.Entry entry = null; + NotificationEntry entry = null; if (expandView instanceof ExpandableNotificationRow) { entry = ((ExpandableNotificationRow) expandView).getEntry(); entry.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 8d1b911b101f..4f61009095c7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -60,10 +60,10 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.NotificationActivityStarter; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.policy.HeadsUpUtil; import com.android.systemui.statusbar.policy.KeyguardMonitor; @@ -131,7 +131,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mEntryManager.addNotificationEntryListener(new NotificationEntryListener() { @Override - public void onPendingEntryAdded(NotificationData.Entry entry) { + public void onPendingEntryAdded(NotificationEntry entry) { handleFullScreenIntent(entry); } }); @@ -267,7 +267,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit } } Intent fillInIntent = null; - NotificationData.Entry entry = row.getEntry(); + NotificationEntry entry = row.getEntry(); CharSequence remoteInputText = null; if (!TextUtils.isEmpty(entry.remoteInputText)) { remoteInputText = entry.remoteInputText; @@ -345,7 +345,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit }, null, false /* afterKeyguardGone */); } - private void handleFullScreenIntent(NotificationData.Entry entry) { + private void handleFullScreenIntent(NotificationEntry entry) { boolean isHeadsUped = mNotificationInterruptionStateProvider.shouldHeadsUp(entry); if (!isHeadsUped && entry.notification.getNotification().fullScreenIntent != null) { if (shouldSuppressFullScreenIntent(entry)) { @@ -413,7 +413,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit || !mActivityLaunchAnimator.isAnimationPending(); } - private boolean shouldSuppressFullScreenIntent(NotificationData.Entry entry) { + private boolean shouldSuppressFullScreenIntent(NotificationEntry entry) { if (mPresenter.isDeviceInVrMode()) { return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index fb3157a128d6..16c8e6232b00 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -60,12 +60,12 @@ import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.AboveShelfObserver; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.NotificationAlertingManager; -import com.android.systemui.statusbar.notification.NotificationData.Entry; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.NotificationRowBinder; import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -183,19 +183,19 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, Dependency.get(InitController.class).addPostInitTask(() -> { NotificationEntryListener notificationEntryListener = new NotificationEntryListener() { @Override - public void onNotificationAdded(Entry entry) { + public void onNotificationAdded(NotificationEntry entry) { // Recalculate the position of the sliding windows and the titles. mShadeController.updateAreThereNotifications(); } @Override - public void onEntryUpdated(Entry entry) { + public void onEntryUpdated(NotificationEntry entry) { mShadeController.updateAreThereNotifications(); } @Override public void onEntryRemoved( - @Nullable Entry entry, + @Nullable NotificationEntry entry, NotificationVisibility visibility, boolean removedByUser) { StatusBarNotificationPresenter.this.onNotificationRemoved( @@ -258,10 +258,10 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, } private void updateNotificationOnUiModeChanged() { - ArrayList<Entry> userNotifications + ArrayList<NotificationEntry> userNotifications = mEntryManager.getNotificationData().getNotificationsForCurrentUser(); for (int i = 0; i < userNotifications.size(); i++) { - Entry entry = userNotifications.get(i); + NotificationEntry entry = userNotifications.get(i); ExpandableNotificationRow row = entry.getRow(); if (row != null) { row.onUiModeChanged(); @@ -323,7 +323,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty(); } - public boolean canHeadsUp(Entry entry, StatusBarNotification sbn) { + public boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn) { if (mShadeController.isDozing()) { return false; } @@ -381,7 +381,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, } @Override - public void onBindRow(Entry entry, PackageManager pmUser, + public void onBindRow(NotificationEntry entry, PackageManager pmUser, StatusBarNotification sbn, ExpandableNotificationRow row) { row.setAboveShelfChangedListener(mAboveShelfObserver); row.setSecureStateProvider(mUnlockMethodCache::canSkipBouncer); @@ -439,7 +439,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, } @Override - public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) { + public void onExpandClicked(NotificationEntry clickedEntry, boolean nowExpanded) { mHeadsUpManager.setExpanded(clickedEntry, nowExpanded); if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD && nowExpanded) { mShadeController.goToLockedShade(clickedEntry.getRow()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java index a02c9d51fecf..fd3f680e5e77 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java @@ -30,7 +30,7 @@ import android.util.Log; import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; import com.android.systemui.statusbar.AlertingNotificationManager; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag; import java.io.FileDescriptor; @@ -108,11 +108,11 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { } } - protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationData.Entry entry) { + protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationEntry entry) { return hasFullScreenIntent(entry); } - protected boolean hasFullScreenIntent(@NonNull NotificationData.Entry entry) { + protected boolean hasFullScreenIntent(@NonNull NotificationEntry entry) { return entry.notification.getNotification().fullScreenIntent != null; } @@ -121,7 +121,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "setEntryPinned: " + isPinned); } - NotificationData.Entry entry = headsUpEntry.mEntry; + NotificationEntry entry = headsUpEntry.mEntry; if (entry.isRowPinned() != isPinned) { entry.setRowPinned(isPinned); updatePinnedMode(); @@ -141,7 +141,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { @Override protected void onAlertEntryAdded(AlertEntry alertEntry) { - NotificationData.Entry entry = alertEntry.mEntry; + NotificationEntry entry = alertEntry.mEntry; entry.setHeadsUp(true); setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(entry)); for (OnHeadsUpChangedListener listener : mListeners) { @@ -151,7 +151,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { @Override protected void onAlertEntryRemoved(AlertEntry alertEntry) { - NotificationData.Entry entry = alertEntry.mEntry; + NotificationEntry entry = alertEntry.mEntry; entry.setHeadsUp(false); setEntryPinned((HeadsUpEntry) alertEntry, false /* isPinned */); for (OnHeadsUpChangedListener listener : mListeners) { @@ -222,7 +222,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { * Returns the top Heads Up Notification, which appears to show at first. */ @Nullable - public NotificationData.Entry getTopEntry() { + public NotificationEntry getTopEntry() { HeadsUpEntry topEntry = getTopHeadsUpEntry(); return (topEntry != null) ? topEntry.mEntry : null; } @@ -323,7 +323,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { * @return -1 if the first argument should be ranked higher than the second, 1 if the second * one should be ranked higher and 0 if they are equal. */ - public int compare(@NonNull NotificationData.Entry a, @NonNull NotificationData.Entry b) { + public int compare(@NonNull NotificationEntry a, @NonNull NotificationEntry b) { AlertEntry aEntry = getHeadsUpEntry(a.key); AlertEntry bEntry = getHeadsUpEntry(b.key); if (aEntry == null || bEntry == null) { @@ -336,7 +336,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { * Set an entry to be expanded and therefore stick in the heads up area if it's pinned * until it's collapsed again. */ - public void setExpanded(@NonNull NotificationData.Entry entry, boolean expanded) { + public void setExpanded(@NonNull NotificationEntry entry, boolean expanded) { HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key); if (headsUpEntry != null && entry.isRowPinned()) { headsUpEntry.setExpanded(expanded); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java index 7ad547afdb53..438226ae082d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java @@ -16,8 +16,7 @@ package com.android.systemui.statusbar.policy; -import com.android.systemui.statusbar.notification.NotificationData; -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; /** * A listener to heads up changes @@ -33,12 +32,12 @@ public interface OnHeadsUpChangedListener { /** * A notification was just pinned to the top. */ - default void onHeadsUpPinned(NotificationData.Entry entry) {} + default void onHeadsUpPinned(NotificationEntry entry) {} /** * A notification was just unpinned from the top. */ - default void onHeadsUpUnPinned(NotificationData.Entry entry) {} + default void onHeadsUpUnPinned(NotificationEntry entry) {} /** * A notification just became a heads up or turned back to its normal state. @@ -46,5 +45,5 @@ public interface OnHeadsUpChangedListener { * @param entry the entry of the changed notification * @param isHeadsUp whether the notification is now a headsUp notification */ - default void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {} + default void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {} } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index 866015e84af0..dfb02cffc6e0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -56,7 +56,7 @@ import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.statusbar.RemoteInputController; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; @@ -83,7 +83,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private RemoteInputController mController; private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler; - private NotificationData.Entry mEntry; + private NotificationEntry mEntry; private boolean mRemoved; @@ -180,7 +180,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } public static RemoteInputView inflate(Context context, ViewGroup root, - NotificationData.Entry entry, + NotificationEntry entry, RemoteInputController controller) { RemoteInputView v = (RemoteInputView) LayoutInflater.from(context).inflate(R.layout.remote_input, root, false); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java index f85d8038ab3f..5e5fc42ddeea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java @@ -36,8 +36,8 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.SmartReplyController; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationUtils; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import java.text.BreakIterator; @@ -194,7 +194,7 @@ public class SmartReplyView extends ViewGroup { */ public void addRepliesFromRemoteInput( SmartReplies smartReplies, - SmartReplyController smartReplyController, NotificationData.Entry entry) { + SmartReplyController smartReplyController, NotificationEntry entry) { if (smartReplies.remoteInput != null && smartReplies.pendingIntent != null) { if (smartReplies.choices != null) { for (int i = 0; i < smartReplies.choices.length; ++i) { @@ -212,7 +212,7 @@ public class SmartReplyView extends ViewGroup { * notification are shown. */ public void addSmartActions(SmartActions smartActions, - SmartReplyController smartReplyController, NotificationData.Entry entry, + SmartReplyController smartReplyController, NotificationEntry entry, HeadsUpManager headsUpManager) { int numSmartActions = smartActions.actions.size(); for (int n = 0; n < numSmartActions; n++) { @@ -235,7 +235,7 @@ public class SmartReplyView extends ViewGroup { @VisibleForTesting Button inflateReplyButton(Context context, ViewGroup root, int replyIndex, SmartReplies smartReplies, SmartReplyController smartReplyController, - NotificationData.Entry entry) { + NotificationEntry entry) { Button b = (Button) LayoutInflater.from(context).inflate( R.layout.smart_reply_button, root, false); CharSequence choice = smartReplies.choices[replyIndex]; @@ -289,7 +289,7 @@ public class SmartReplyView extends ViewGroup { @VisibleForTesting Button inflateActionButton(Context context, ViewGroup root, int actionIndex, SmartActions smartActions, SmartReplyController smartReplyController, - NotificationData.Entry entry, HeadsUpManager headsUpManager) { + NotificationEntry entry, HeadsUpManager headsUpManager) { Notification.Action action = smartActions.actions.get(actionIndex); Button button = (Button) LayoutInflater.from(context).inflate( R.layout.smart_action_button, root, false); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java index 415060244243..1844df5c070a 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java @@ -35,6 +35,8 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; import android.text.TextPaint; import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.TextClock; @@ -42,6 +44,8 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ClockPlugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.StatusBarStateController; import org.junit.Before; import org.junit.Test; @@ -60,6 +64,7 @@ import org.mockito.MockitoAnnotations; public class KeyguardClockSwitchTest extends SysuiTestCase { private PluginManager mPluginManager; private FrameLayout mClockContainer; + private StatusBarStateController.StateListener mStateListener; @Mock TextClock mClockView; @@ -75,6 +80,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { mClockContainer = mKeyguardClockSwitch.findViewById(R.id.clock_view); MockitoAnnotations.initMocks(this); when(mClockView.getPaint()).thenReturn(mock(TextPaint.class)); + mStateListener = mKeyguardClockSwitch.getStateListener(); } @Test @@ -272,4 +278,28 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { verify(plugin).setStyle(style); } + + @Test + public void onStateChanged_InvisibleInShade() { + // GIVEN that the big clock container is visible + ViewGroup container = mock(ViewGroup.class); + when(container.getVisibility()).thenReturn(View.VISIBLE); + mKeyguardClockSwitch.setBigClockContainer(container); + // WHEN transitioned to SHADE state + mStateListener.onStateChanged(StatusBarState.SHADE); + // THEN the container is invisible. + verify(container).setVisibility(View.INVISIBLE); + } + + @Test + public void onStateChanged_VisibleInKeyguard() { + // GIVEN that the big clock container is invisible + ViewGroup container = mock(ViewGroup.class); + when(container.getVisibility()).thenReturn(View.INVISIBLE); + mKeyguardClockSwitch.setBigClockContainer(container); + // WHEN transitioned to KEYGUARD state + mStateListener.onStateChanged(StatusBarState.KEYGUARD); + // THEN the container is visible. + verify(container).setVisibility(View.VISIBLE); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java index f8912bcca302..9400d6d723e9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java @@ -36,9 +36,9 @@ import android.support.test.runner.AndroidJUnit4; import android.widget.RemoteViews; import com.android.internal.messages.nano.SystemMessageProto; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import org.junit.Before; import org.junit.Test; @@ -391,18 +391,18 @@ public class ForegroundServiceControllerTest extends SysuiTestCase { } private void entryRemoved(StatusBarNotification notification) { - mEntryListener.onEntryRemoved(new NotificationData.Entry(notification), + mEntryListener.onEntryRemoved(new NotificationEntry(notification), null, false); } private void entryAdded(StatusBarNotification notification, int importance) { - NotificationData.Entry entry = new NotificationData.Entry(notification); + NotificationEntry entry = new NotificationEntry(notification); entry.importance = importance; mEntryListener.onPendingEntryAdded(entry); } private void entryUpdated(StatusBarNotification notification, int importance) { - NotificationData.Entry entry = new NotificationData.Entry(notification); + NotificationEntry entry = new NotificationEntry(notification); entry.importance = importance; mEntryListener.onEntryUpdated(entry); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java index 31df4a3fc766..21d3652adca7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -33,9 +33,9 @@ import android.widget.FrameLayout; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.NotificationTestHelper; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotificationData; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.StatusBarWindowController; diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java index a8ff0b21bab9..cfc19ef28c92 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java @@ -21,6 +21,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -44,6 +45,7 @@ import androidx.slice.core.SliceQuery; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.NotificationMediaManager; +import com.android.systemui.statusbar.StatusBarStateController; import org.junit.Assert; import org.junit.Before; @@ -67,6 +69,8 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { private AlarmManager mAlarmManager; @Mock private NotificationMediaManager mNotificationMediaManager; + @Mock + private StatusBarStateController mStatusBarStateController; private TestableKeyguardSliceProvider mProvider; private boolean mIsZenMode; @@ -76,7 +80,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { mIsZenMode = false; mProvider = new TestableKeyguardSliceProvider(); mProvider.attachInfo(getContext(), null); - mProvider.initDependencies(); + mProvider.initDependencies(mNotificationMediaManager, mStatusBarStateController); SliceProvider.setSpecs(new HashSet<>(Arrays.asList(SliceSpecs.LIST))); } @@ -98,6 +102,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { @Test public void onBindSlice_readsMedia() { MediaMetadata metadata = mock(MediaMetadata.class); + mProvider.onDozingChanged(true); mProvider.onMetadataChanged(metadata); mProvider.onBindSlice(mProvider.getUri()); verify(metadata).getText(eq(MediaMetadata.METADATA_KEY_TITLE)); @@ -162,7 +167,31 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { @Test public void onMetadataChanged_updatesSlice() { mProvider.onMetadataChanged(mock(MediaMetadata.class)); + mProvider.onDozingChanged(true); + verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null)); + + // Hides after waking up + reset(mContentResolver); + mProvider.onDozingChanged(false); + verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null)); + + // And won't update slice if device is awake + reset(mContentResolver); + mProvider.onMetadataChanged(mock(MediaMetadata.class)); + verify(mContentResolver, never()).notifyChange(eq(mProvider.getUri()), eq(null)); + } + + @Test + public void onDozingChanged_updatesSliceIfMedia() { + // Show media when dozing + mProvider.onMetadataChanged(mock(MediaMetadata.class)); + mProvider.onDozingChanged(true); verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null)); + + // Do not notify again if nothing changed + reset(mContentResolver); + mProvider.onDozingChanged(true); + verify(mContentResolver, never()).notifyChange(eq(mProvider.getUri()), eq(null)); } private class TestableKeyguardSliceProvider extends KeyguardSliceProvider { @@ -201,11 +230,6 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { protected String getFormattedDateLocked() { return super.getFormattedDateLocked() + mCounter++; } - - @Override - public void initDependencies() { - mMediaManager = mNotificationMediaManager; - } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java index 9d0556f61398..f8957b204021 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java @@ -36,7 +36,7 @@ import android.testing.TestableLooper; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -67,7 +67,7 @@ public class AlertingNotificationManagerTest extends SysuiTestCase { private AlertingNotificationManager mAlertingNotificationManager; - protected NotificationData.Entry mEntry; + protected NotificationEntry mEntry; protected Handler mTestHandler; private StatusBarNotification mSbn; protected boolean mTimedOut = false; @@ -119,7 +119,7 @@ public class AlertingNotificationManagerTest extends SysuiTestCase { public void setUp() { mTestHandler = Handler.createAsync(Looper.myLooper()); mSbn = createNewNotification(0 /* id */); - mEntry = new NotificationData.Entry(mSbn); + mEntry = new NotificationEntry(mSbn); mEntry.setRow(mRow); mAlertingNotificationManager = createAlertingNotificationManager(); @@ -170,7 +170,7 @@ public class AlertingNotificationManagerTest extends SysuiTestCase { public void testReleaseAllImmediately() { for (int i = 0; i < TEST_NUM_NOTIFICATIONS; i++) { StatusBarNotification sbn = createNewNotification(i); - NotificationData.Entry entry = new NotificationData.Entry(sbn); + NotificationEntry entry = new NotificationEntry(sbn); entry.setRow(mRow); mAlertingNotificationManager.showNotification(entry); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java index 65c04fe4bcd3..c880172e775a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java @@ -31,8 +31,9 @@ import android.testing.TestableLooper; import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import org.junit.Before; import org.junit.Test; @@ -85,7 +86,7 @@ public class NotificationListenerTest extends SysuiTestCase { @Test public void testNotificationUpdateCallsUpdateNotification() { - when(mNotificationData.get(mSbn.getKey())).thenReturn(new NotificationData.Entry(mSbn)); + when(mNotificationData.get(mSbn.getKey())).thenReturn(new NotificationEntry(mSbn)); mListener.onNotificationPosted(mSbn, mRanking); TestableLooper.get(this).processAllMessages(); verify(mEntryManager).updateNotification(mSbn, mRanking); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java index f168a490acf4..d7ca5b410e56 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar; -import static android.content.Intent.ACTION_DEVICE_LOCKED_CHANGED; import static android.content.Intent.ACTION_USER_SWITCHED; import static junit.framework.Assert.assertFalse; @@ -42,8 +41,8 @@ import android.testing.TestableLooper; import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotificationData; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.policy.DeviceProvisionedController; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java index 150dcb9f841c..c159516d5bde 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java @@ -24,8 +24,8 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.NotificationRemoteInputManager.RemoteInputActiveExtender; import com.android.systemui.statusbar.NotificationRemoteInputManager.RemoteInputHistoryExtender; import com.android.systemui.statusbar.NotificationRemoteInputManager.SmartReplyHistoryExtender; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.phone.ShadeController; @@ -60,7 +60,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { private TestableNotificationRemoteInputManager mRemoteInputManager; private StatusBarNotification mSbn; - private NotificationData.Entry mEntry; + private NotificationEntry mEntry; private RemoteInputHistoryExtender mRemoteInputHistoryExtender; private SmartReplyHistoryExtender mSmartReplyHistoryExtender; private RemoteInputActiveExtender mRemoteInputActiveExtender; @@ -75,7 +75,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { Handler.createAsync(Looper.myLooper())); mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, new Notification(), UserHandle.CURRENT, null, 0); - mEntry = new NotificationData.Entry(mSbn); + mEntry = new NotificationEntry(mSbn); mEntry.setRow(mRow); mRemoteInputManager.setUpWithPresenterForTest(mCallback, @@ -170,7 +170,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { // Setup a notification entry with 1 remote input. StatusBarNotification newSbn = mRemoteInputManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", false); - NotificationData.Entry entry = new NotificationData.Entry(newSbn); + NotificationEntry entry = new NotificationEntry(newSbn); // Try rebuilding to add another reply. newSbn = mRemoteInputManager.rebuildNotificationWithRemoteInput(entry, "Reply 2", true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java index fb5e8753c935..529da8251c57 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java @@ -32,7 +32,7 @@ import android.view.LayoutInflater; import android.widget.RemoteViews; import com.android.systemui.R; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag; import com.android.systemui.statusbar.notification.row.NotificationInflaterTest; @@ -238,7 +238,7 @@ public class NotificationTestHelper { mUser, null /* overrideGroupKey */, System.currentTimeMillis()); - NotificationData.Entry entry = new NotificationData.Entry(sbn); + NotificationEntry entry = new NotificationEntry(sbn); entry.setRow(row); entry.createIcons(mContext, sbn); entry.channel = new NotificationChannel( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java index e84e0f30c745..bf91305137e2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java @@ -38,10 +38,10 @@ import com.android.systemui.InitController; import com.android.systemui.SysuiTestCase; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; -import com.android.systemui.statusbar.notification.NotificationData; -import com.android.systemui.statusbar.notification.NotificationData.Entry; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; @@ -102,9 +102,9 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer); } - private NotificationData.Entry createEntry() throws Exception { + private NotificationEntry createEntry() throws Exception { ExpandableNotificationRow row = mHelper.createRow(); - NotificationData.Entry entry = new NotificationData.Entry(row.getStatusBarNotification()); + NotificationEntry entry = new NotificationEntry(row.getStatusBarNotification()); entry.setRow(row); return entry; } @@ -113,9 +113,9 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { public void testNotificationsBecomingBundled() throws Exception { // Tests 3 top level notifications becoming a single bundled notification with |entry0| as // the summary. - NotificationData.Entry entry0 = createEntry(); - NotificationData.Entry entry1 = createEntry(); - NotificationData.Entry entry2 = createEntry(); + NotificationEntry entry0 = createEntry(); + NotificationEntry entry1 = createEntry(); + NotificationEntry entry2 = createEntry(); // Set up the prior state to look like three top level notifications. mListContainer.addContainerView(entry0.getRow()); @@ -142,9 +142,9 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { @Test public void testNotificationsBecomingUnbundled() throws Exception { // Tests a bundled notification becoming three top level notifications. - NotificationData.Entry entry0 = createEntry(); - NotificationData.Entry entry1 = createEntry(); - NotificationData.Entry entry2 = createEntry(); + NotificationEntry entry0 = createEntry(); + NotificationEntry entry1 = createEntry(); + NotificationEntry entry2 = createEntry(); entry0.getRow().addChildNotification(entry1.getRow()); entry0.getRow().addChildNotification(entry2.getRow()); @@ -173,8 +173,8 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { @Test public void testNotificationsBecomingSuppressed() throws Exception { // Tests two top level notifications becoming a suppressed summary and a child. - NotificationData.Entry entry0 = createEntry(); - NotificationData.Entry entry1 = createEntry(); + NotificationEntry entry0 = createEntry(); + NotificationEntry entry1 = createEntry(); entry0.getRow().addChildNotification(entry1.getRow()); // Set up the prior state to look like a top level notification. @@ -199,7 +199,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { @Test public void testUpdateNotificationViews_appOps() throws Exception { - NotificationData.Entry entry0 = createEntry(); + NotificationEntry entry0 = createEntry(); entry0.setRow(spy(entry0.getRow())); when(mNotificationData.getActiveNotifications()).thenReturn( Lists.newArrayList(entry0)); @@ -264,7 +264,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { public void setMaxDisplayedNotifications(int maxNotifications) {} @Override - public ViewGroup getViewParentForNotification(NotificationData.Entry entry) { + public ViewGroup getViewParentForNotification(NotificationEntry entry) { return null; } @@ -280,10 +280,10 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { } @Override - public void cleanUpViewStateForEntry(Entry entry) { } + public void cleanUpViewStateForEntry(NotificationEntry entry) { } @Override - public boolean isInVisibleLocation(Entry entry) { + public boolean isInVisibleLocation(NotificationEntry entry) { return true; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java index e7a1f051a6df..6cca434bd8cb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java @@ -36,8 +36,8 @@ import android.testing.TestableLooper; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.phone.ShadeController; import org.junit.Before; @@ -58,7 +58,7 @@ public class SmartReplyControllerTest extends SysuiTestCase { private static final int TEST_ACTION_COUNT = 3; private Notification mNotification; - private NotificationData.Entry mEntry; + private NotificationEntry mEntry; private SmartReplyController mSmartReplyController; private NotificationRemoteInputManager mRemoteInputManager; @@ -92,7 +92,7 @@ public class SmartReplyControllerTest extends SysuiTestCase { mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, mNotification, new UserHandle(ActivityManager.getCurrentUser()), null, 0); - mEntry = new NotificationData.Entry(mSbn); + mEntry = new NotificationEntry(mSbn); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index 6197341acda5..4d22536f34f0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -65,7 +65,8 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.StatusBarIconView; -import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment; +import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationInflater; @@ -123,7 +124,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Mock private RowInflaterTask mAsyncInflationTask; @Mock private NotificationRowBinder mMockedRowBinder; - private NotificationData.Entry mEntry; + private NotificationEntry mEntry; private StatusBarNotification mSbn; private TestableNotificationEntryManager mEntryManager; private CountDownLatch mCountDownLatch; @@ -137,7 +138,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { } @Override - public void onAsyncInflationFinished(NotificationData.Entry entry, + public void onAsyncInflationFinished(NotificationEntry entry, @NotificationInflater.InflationFlag int inflatedFlags) { super.onAsyncInflationFinished(entry, inflatedFlags); @@ -222,7 +223,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { .setContentText("Text"); mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, n.build(), new UserHandle(ActivityManager.getCurrentUser()), null, 0); - mEntry = new NotificationData.Entry(mSbn); + mEntry = new NotificationEntry(mSbn); mEntry.expandedIcon = mock(StatusBarIconView.class); mEntryManager = new TestableNotificationEntryManager(mContext); @@ -259,10 +260,10 @@ public class NotificationEntryManagerTest extends SysuiTestCase { verify(mEntryListener, never()).onInflationError(any(), any()); // Row inflation: - ArgumentCaptor<NotificationData.Entry> entryCaptor = ArgumentCaptor.forClass( - NotificationData.Entry.class); + ArgumentCaptor<NotificationEntry> entryCaptor = ArgumentCaptor.forClass( + NotificationEntry.class); verify(mBindCallback).onBindRow(entryCaptor.capture(), any(), eq(mSbn), any()); - NotificationData.Entry entry = entryCaptor.getValue(); + NotificationEntry entry = entryCaptor.getValue(); verify(mRemoteInputManager).bindRow(entry.getRow()); // Row content inflation: diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java index da8bc01dc29d..387475f79cc2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java @@ -40,6 +40,8 @@ import android.testing.TestableLooper.RunWithLooper; import com.android.systemui.ForegroundServiceController; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.NotificationTestHelper; +import com.android.systemui.statusbar.notification.collection.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ShadeController; @@ -191,12 +193,12 @@ public class NotificationFilterTest extends SysuiTestCase { // test should filter out hidden notifications: // hidden - NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification); + NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification); entry.suspended = true; assertTrue(mNotificationFilter.shouldFilterOut(entry)); // not hidden - entry = new NotificationData.Entry(mMockStatusBarNotification); + entry = new NotificationEntry(mMockStatusBarNotification); entry.suspended = false; assertFalse(mNotificationFilter.shouldFilterOut(entry)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java index 6fbd8c751285..813fc9d308e5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java @@ -29,6 +29,7 @@ import android.test.suitebuilder.annotation.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.NotificationPresenter; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import org.junit.Before; @@ -44,13 +45,13 @@ public class VisualStabilityManagerTest extends SysuiTestCase { private VisualStabilityManager.Callback mCallback = mock(VisualStabilityManager.Callback.class); private VisibilityLocationProvider mLocationProvider = mock(VisibilityLocationProvider.class); private ExpandableNotificationRow mRow = mock(ExpandableNotificationRow.class); - private NotificationData.Entry mEntry; + private NotificationEntry mEntry; @Before public void setUp() { mVisualStabilityManager.setUpWithPresenter(mock(NotificationPresenter.class)); mVisualStabilityManager.setVisibilityLocationProvider(mLocationProvider); - mEntry = new NotificationData.Entry(mock(StatusBarNotification.class)); + mEntry = new NotificationEntry(mock(StatusBarNotification.class)); mEntry.setRow(mRow); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java index 6f4de1cc6dd8..193f483058d6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.systemui.statusbar.notification; +package com.android.systemui.statusbar.notification.collection; import static android.app.AppOpsManager.OP_ACCEPT_HANDOVER; import static android.app.AppOpsManager.OP_CAMERA; @@ -59,7 +59,9 @@ import com.android.systemui.ForegroundServiceController; import com.android.systemui.InitController; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.NotificationTestHelper; -import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment; +import com.android.systemui.statusbar.notification.collection.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ShadeController; @@ -203,7 +205,7 @@ public class NotificationDataTest extends SysuiTestCase { mRow.getEntry().notification)).thenReturn(false); when(mEnvironment.isNotificationForCurrentProfiles( row2.getEntry().notification)).thenReturn(true); - ArrayList<NotificationData.Entry> result = + ArrayList<NotificationEntry> result = mNotificationData.getNotificationsForCurrentUser(); assertEquals(result.size(), 1); @@ -217,7 +219,7 @@ public class NotificationDataTest extends SysuiTestCase { TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY); Notification n = mMockStatusBarNotification.getNotification(); n.flags = Notification.FLAG_FOREGROUND_SERVICE; - NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification); + NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification); mNotificationData.add(entry); assertTrue(entry.isExemptFromDndVisualSuppression()); @@ -234,7 +236,7 @@ public class NotificationDataTest extends SysuiTestCase { nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class))); n = nb.build(); when(mMockStatusBarNotification.getNotification()).thenReturn(n); - NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification); + NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification); mNotificationData.add(entry); assertTrue(entry.isExemptFromDndVisualSuppression()); @@ -246,7 +248,7 @@ public class NotificationDataTest extends SysuiTestCase { initStatusBarNotification(false); when(mMockStatusBarNotification.getKey()).thenReturn( TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY); - NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification); + NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification); entry.mIsSystemNotification = true; mNotificationData.add(entry); @@ -259,7 +261,7 @@ public class NotificationDataTest extends SysuiTestCase { initStatusBarNotification(false); when(mMockStatusBarNotification.getKey()).thenReturn( TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY); - NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification); + NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification); entry.mIsSystemNotification = true; mNotificationData.add(entry); @@ -313,8 +315,8 @@ public class NotificationDataTest extends SysuiTestCase { snoozeCriterions.add(snoozeCriterion); when(ranking.getSnoozeCriteria()).thenReturn(snoozeCriterions); - NotificationData.Entry entry = - new NotificationData.Entry(mMockStatusBarNotification, ranking); + NotificationEntry entry = + new NotificationEntry(mMockStatusBarNotification, ranking); assertEquals(systemGeneratedSmartActions, entry.systemGeneratedSmartActions); assertEquals(NOTIFICATION_CHANNEL, entry.channel); @@ -343,7 +345,7 @@ public class NotificationDataTest extends SysuiTestCase { StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, notification, mContext.getUser(), "", 0); - NotificationData.Entry entry = new NotificationData.Entry(sbn); + NotificationEntry entry = new NotificationEntry(sbn); entry.setHasSentReply(); assertTrue(entry.isLastMessageFromReply()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java index afdeb62a8f28..64725896cce5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java @@ -41,9 +41,10 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.UiOffloadThread; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; @@ -77,7 +78,7 @@ public class NotificationLoggerTest extends SysuiTestCase { @Mock private NotificationListener mListener; @Captor private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor; - private NotificationData.Entry mEntry; + private NotificationEntry mEntry; private TestableNotificationLogger mLogger; private NotificationEntryListener mNotificationEntryListener; private ConcurrentLinkedQueue<AssertionError> mErrorQueue = new ConcurrentLinkedQueue<>(); @@ -93,7 +94,7 @@ public class NotificationLoggerTest extends SysuiTestCase { StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, new Notification(), UserHandle.CURRENT, null, 0); - mEntry = new NotificationData.Entry(sbn); + mEntry = new NotificationEntry(sbn); mEntry.setRow(mRow); mLogger = new TestableNotificationLogger(mListener, Dependency.get(UiOffloadThread.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java index 82b42c51622d..d94bf845ca5b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java @@ -47,7 +47,7 @@ import android.view.View; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.policy.SmartReplyConstants; import org.junit.Before; @@ -73,7 +73,7 @@ public class NotificationContentViewTest extends SysuiTestCase { StatusBarNotification mStatusBarNotification; @Mock Notification mNotification; - NotificationData.Entry mEntry; + NotificationEntry mEntry; @Mock RemoteInput mRemoteInput; @Mock @@ -107,7 +107,7 @@ public class NotificationContentViewTest extends SysuiTestCase { // Smart replies and actions when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(true); when(mStatusBarNotification.getNotification()).thenReturn(mNotification); - mEntry = new NotificationData.Entry(mStatusBarNotification); + mEntry = new NotificationEntry(mStatusBarNotification); when(mSmartReplyConstants.isEnabled()).thenReturn(true); mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java index ad43bea74089..0899c73ab07b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java @@ -60,7 +60,7 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationTestHelper; import com.android.systemui.statusbar.notification.NotificationActivityStarter; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -183,8 +183,8 @@ public class NotificationGutsManagerTest extends SysuiTestCase { when(row.getGuts()).thenReturn(guts); doNothing().when(row).inflateGuts(); - NotificationData.Entry realEntry = realRow.getEntry(); - NotificationData.Entry entry = spy(realEntry); + NotificationEntry realEntry = realRow.getEntry(); + NotificationEntry entry = spy(realEntry); when(entry.getRow()).thenReturn(row); when(entry.getGuts()).thenReturn(guts); @@ -443,7 +443,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { NotificationGuts guts = new NotificationGuts(mContext); ExpandableNotificationRow row = spy(createTestNotificationRow()); doReturn(guts).when(row).getGuts(); - NotificationData.Entry entry = row.getEntry(); + NotificationEntry entry = row.getEntry(); entry.setRow(row); mGutsManager.setExposedGuts(guts); @@ -452,7 +452,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { @Test public void testSetShouldManageLifetime_setShouldManage() { - NotificationData.Entry entry = createTestNotificationRow().getEntry(); + NotificationEntry entry = createTestNotificationRow().getEntry(); mGutsManager.setShouldManageLifetime(entry, true /* shouldManage */); assertTrue(entry.key.equals(mGutsManager.mKeyToRemoveOnGutsClosed)); @@ -460,7 +460,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { @Test public void testSetShouldManageLifetime_setShouldNotManage() { - NotificationData.Entry entry = createTestNotificationRow().getEntry(); + NotificationEntry entry = createTestNotificationRow().getEntry(); mGutsManager.mKeyToRemoveOnGutsClosed = entry.key; mGutsManager.setShouldManageLifetime(entry, false /* shouldManage */); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java index d6b706d73827..648df3cab33c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java @@ -48,7 +48,7 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.InflationTask; import com.android.systemui.statusbar.NotificationTestHelper; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import org.junit.Assert; import org.junit.Before; @@ -88,7 +88,7 @@ public class NotificationInflaterTest extends SysuiTestCase { } @Override - public void onAsyncInflationFinished(NotificationData.Entry entry, + public void onAsyncInflationFinished(NotificationEntry entry, @NotificationInflater.InflationFlag int inflatedFlags) { } }); @@ -174,7 +174,7 @@ public class NotificationInflaterTest extends SysuiTestCase { } @Override - public void onAsyncInflationFinished(NotificationData.Entry entry, + public void onAsyncInflationFinished(NotificationEntry entry, @NotificationInflater.InflationFlag int inflatedFlags) { countDownLatch.countDown(); } @@ -256,7 +256,7 @@ public class NotificationInflaterTest extends SysuiTestCase { } @Override - public void onAsyncInflationFinished(NotificationData.Entry entry, + public void onAsyncInflationFinished(NotificationEntry entry, @NotificationInflater.InflationFlag int inflatedFlags) { if (expectingException) { exceptionHolder.setException(new RuntimeException( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java index e4d019656072..04d7509f760f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java @@ -35,7 +35,7 @@ import android.testing.ViewUtils; import android.view.ViewGroup; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.utils.leaks.LeakCheckedTest; import org.junit.Before; @@ -54,7 +54,7 @@ public class NotificationMenuRowTest extends LeakCheckedTest { public void setup() { injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES); mRow = mock(ExpandableNotificationRow.class); - NotificationData.Entry entry = new NotificationData.Entry( + NotificationEntry entry = new NotificationEntry( mock(StatusBarNotification.class)); entry.channel = mock(NotificationChannel.class); when(mRow.getEntry()).thenReturn(entry); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index b66d0ab78fd8..214404c8c295 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -53,9 +53,9 @@ import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.notification.NotificationData; -import com.android.systemui.statusbar.notification.NotificationData.Entry; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.FooterView; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; @@ -265,8 +265,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test public void testUpdateFooter_remoteInput() { setBarStateForTest(StatusBarState.SHADE); - ArrayList<Entry> entries = new ArrayList<>(); - entries.add(mock(Entry.class)); + ArrayList<NotificationEntry> entries = new ArrayList<>(); + entries.add(mock(NotificationEntry.class)); when(mNotificationData.getActiveNotifications()).thenReturn(entries); ExpandableNotificationRow row = mock(ExpandableNotificationRow.class); @@ -282,8 +282,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test public void testUpdateFooter_oneClearableNotification() { setBarStateForTest(StatusBarState.SHADE); - ArrayList<Entry> entries = new ArrayList<>(); - entries.add(mock(Entry.class)); + ArrayList<NotificationEntry> entries = new ArrayList<>(); + entries.add(mock(NotificationEntry.class)); when(mNotificationData.getActiveNotifications()).thenReturn(entries); ExpandableNotificationRow row = mock(ExpandableNotificationRow.class); @@ -298,8 +298,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test public void testUpdateFooter_oneNonClearableNotification() { setBarStateForTest(StatusBarState.SHADE); - ArrayList<Entry> entries = new ArrayList<>(); - entries.add(mock(Entry.class)); + ArrayList<NotificationEntry> entries = new ArrayList<>(); + entries.add(mock(NotificationEntry.class)); when(mEntryManager.getNotificationData().getActiveNotifications()).thenReturn(entries); assertTrue(mEntryManager.getNotificationData().getActiveNotifications().size() != 0); @@ -314,7 +314,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { // add notification ExpandableNotificationRow row = mock(ExpandableNotificationRow.class); - NotificationData.Entry entry = mock(NotificationData.Entry.class); + NotificationEntry entry = mock(NotificationEntry.class); when(row.getEntry()).thenReturn(entry); when(entry.isClearable()).thenReturn(true); mStackScroller.addContainerView(row); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java index 44deb10941ec..9ceb325204fe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java @@ -16,15 +16,20 @@ package com.android.systemui.statusbar.phone; -import android.view.View; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.Mockito.when; + import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.view.View; import com.android.systemui.statusbar.AlertingNotificationManager; import com.android.systemui.statusbar.AlertingNotificationManagerTest; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import org.junit.Before; import org.junit.Rule; @@ -34,11 +39,6 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -import static junit.framework.Assert.assertTrue; -import static junit.framework.Assert.assertFalse; - -import static org.mockito.Mockito.when; - @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @@ -96,7 +96,7 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { @Test public void testCanRemoveImmediately_notTopEntry() { - NotificationData.Entry laterEntry = new NotificationData.Entry(createNewNotification(1)); + NotificationEntry laterEntry = new NotificationEntry(createNewNotification(1)); laterEntry.setRow(mRow); mHeadsUpManager.showNotification(mEntry); mHeadsUpManager.showNotification(laterEntry); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java index 79695fd6d38d..cefd4acbfb5a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java @@ -32,10 +32,9 @@ import android.testing.TestableLooper; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.AmbientPulseManager; -import com.android.systemui.statusbar.notification.NotificationData; -import com.android.systemui.statusbar.notification.NotificationData.Entry; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.policy.HeadsUpManager; import org.junit.Before; @@ -64,7 +63,7 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase { @Captor private ArgumentCaptor<NotificationEntryListener> mListenerCaptor; private NotificationEntryListener mNotificationEntryListener; - private final HashMap<String, Entry> mPendingEntries = new HashMap<>(); + private final HashMap<String, NotificationEntry> mPendingEntries = new HashMap<>(); private final NotificationGroupTestHelper mGroupTestHelper = new NotificationGroupTestHelper(mContext); @@ -94,9 +93,9 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase { @Test public void testSuppressedSummaryHeadsUpTransfersToChild() { - Entry summaryEntry = mGroupTestHelper.createSummaryNotification(); + NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(); mHeadsUpManager.showNotification(summaryEntry); - Entry childEntry = mGroupTestHelper.createChildNotification(); + NotificationEntry childEntry = mGroupTestHelper.createChildNotification(); // Summary will be suppressed because there is only one child. mGroupManager.onEntryAdded(summaryEntry); @@ -109,11 +108,11 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase { @Test public void testSuppressedSummaryHeadsUpTransfersToChildButBackAgain() { - NotificationData.Entry summaryEntry = + NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY); - NotificationData.Entry childEntry = + NotificationEntry childEntry = mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY); - NotificationData.Entry childEntry2 = + NotificationEntry childEntry2 = mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY); mHeadsUpManager.showNotification(summaryEntry); // Trigger a transfer of alert state from summary to child. @@ -133,11 +132,11 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase { @Test public void testSuppressedSummaryHeadsUpDoesntTransferBackOnDozingChanged() { - NotificationData.Entry summaryEntry = + NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY); - NotificationData.Entry childEntry = + NotificationEntry childEntry = mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY); - NotificationData.Entry childEntry2 = + NotificationEntry childEntry2 = mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY); mHeadsUpManager.showNotification(summaryEntry); // Trigger a transfer of alert state from summary to child. @@ -158,9 +157,9 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase { @Test public void testSuppressedSummaryHeadsUpTransferDoesNotAlertChildIfUninflated() { - Entry summaryEntry = mGroupTestHelper.createSummaryNotification(); + NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(); mHeadsUpManager.showNotification(summaryEntry); - Entry childEntry = mGroupTestHelper.createChildNotification(); + NotificationEntry childEntry = mGroupTestHelper.createChildNotification(); when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag())) .thenReturn(false); @@ -176,9 +175,9 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase { @Test public void testSuppressedSummaryHeadsUpTransferAlertsChildOnInflation() { - Entry summaryEntry = mGroupTestHelper.createSummaryNotification(); + NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(); mHeadsUpManager.showNotification(summaryEntry); - Entry childEntry = mGroupTestHelper.createChildNotification(); + NotificationEntry childEntry = mGroupTestHelper.createChildNotification(); when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag())) .thenReturn(false); @@ -196,13 +195,13 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase { @Test public void testSuppressedSummaryHeadsUpTransferBackAbortsChildInflation() { - NotificationData.Entry summaryEntry = + NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY); - NotificationData.Entry childEntry = + NotificationEntry childEntry = mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY); when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag())) .thenReturn(false); - NotificationData.Entry childEntry2 = + NotificationEntry childEntry2 = mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY); mHeadsUpManager.showNotification(summaryEntry); // Trigger a transfer of alert state from summary to child. @@ -226,9 +225,9 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase { @Test public void testCleanUpPendingAlertInfo() { - NotificationData.Entry summaryEntry = + NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY); - NotificationData.Entry childEntry = + NotificationEntry childEntry = mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY); when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag())) .thenReturn(false); @@ -244,9 +243,9 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase { @Test public void testUpdateGroupChangeDoesNotTransfer() { - NotificationData.Entry summaryEntry = + NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY); - NotificationData.Entry childEntry = + NotificationEntry childEntry = mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY); when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag())) .thenReturn(false); @@ -267,9 +266,9 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase { @Test public void testUpdateChildToSummaryDoesNotTransfer() { - NotificationData.Entry summaryEntry = + NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY); - NotificationData.Entry childEntry = + NotificationEntry childEntry = mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY); when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag())) .thenReturn(false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java index b0bd1fba3760..ecb3e4d8067c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java @@ -29,7 +29,7 @@ import android.testing.TestableLooper; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.AmbientPulseManager; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.policy.HeadsUpManager; import org.junit.Before; @@ -69,8 +69,8 @@ public class NotificationGroupManagerTest extends SysuiTestCase { @Test public void testIsOnlyChildInGroup() { - NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification(); - NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification(); + NotificationEntry childEntry = mGroupTestHelper.createChildNotification(); + NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(); mGroupManager.onEntryAdded(summaryEntry); mGroupManager.onEntryAdded(childEntry); @@ -80,8 +80,8 @@ public class NotificationGroupManagerTest extends SysuiTestCase { @Test public void testIsChildInGroupWithSummary() { - NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification(); - NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification(); + NotificationEntry childEntry = mGroupTestHelper.createChildNotification(); + NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(); mGroupManager.onEntryAdded(summaryEntry); mGroupManager.onEntryAdded(childEntry); @@ -92,8 +92,8 @@ public class NotificationGroupManagerTest extends SysuiTestCase { @Test public void testIsSummaryOfGroupWithChildren() { - NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification(); - NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification(); + NotificationEntry childEntry = mGroupTestHelper.createChildNotification(); + NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(); mGroupManager.onEntryAdded(summaryEntry); mGroupManager.onEntryAdded(childEntry); @@ -105,8 +105,8 @@ public class NotificationGroupManagerTest extends SysuiTestCase { @Test public void testRemoveChildFromGroupWithSummary() { - NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification(); - NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification(); + NotificationEntry childEntry = mGroupTestHelper.createChildNotification(); + NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(); mGroupManager.onEntryAdded(summaryEntry); mGroupManager.onEntryAdded(childEntry); mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification()); @@ -118,8 +118,8 @@ public class NotificationGroupManagerTest extends SysuiTestCase { @Test public void testRemoveSummaryFromGroupWithSummary() { - NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification(); - NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification(); + NotificationEntry childEntry = mGroupTestHelper.createChildNotification(); + NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(); mGroupManager.onEntryAdded(summaryEntry); mGroupManager.onEntryAdded(childEntry); mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification()); @@ -132,8 +132,8 @@ public class NotificationGroupManagerTest extends SysuiTestCase { @Test public void testHeadsUpEntryIsIsolated() { - NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification(); - NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification(); + NotificationEntry childEntry = mGroupTestHelper.createChildNotification(); + NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(); mGroupManager.onEntryAdded(summaryEntry); mGroupManager.onEntryAdded(childEntry); mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification()); @@ -149,8 +149,8 @@ public class NotificationGroupManagerTest extends SysuiTestCase { @Test public void testAmbientPulseEntryIsIsolated() { - NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification(); - NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification(); + NotificationEntry childEntry = mGroupTestHelper.createChildNotification(); + NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(); mGroupManager.onEntryAdded(summaryEntry); mGroupManager.onEntryAdded(childEntry); mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java index 7ad68eb1c9a3..e6b9778eb59f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java @@ -27,7 +27,7 @@ import android.os.UserHandle; import android.service.notification.StatusBarNotification; import com.android.systemui.R; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; /** @@ -44,23 +44,23 @@ public final class NotificationGroupTestHelper { mContext = context; } - public NotificationData.Entry createSummaryNotification() { + public NotificationEntry createSummaryNotification() { return createSummaryNotification(Notification.GROUP_ALERT_ALL); } - public NotificationData.Entry createSummaryNotification(int groupAlertBehavior) { + public NotificationEntry createSummaryNotification(int groupAlertBehavior) { return createEntry(true, groupAlertBehavior); } - public NotificationData.Entry createChildNotification() { + public NotificationEntry createChildNotification() { return createChildNotification(Notification.GROUP_ALERT_ALL); } - public NotificationData.Entry createChildNotification(int groupAlertBehavior) { + public NotificationEntry createChildNotification(int groupAlertBehavior) { return createEntry(false, groupAlertBehavior); } - public NotificationData.Entry createEntry(boolean isSummary, int groupAlertBehavior) { + public NotificationEntry createEntry(boolean isSummary, int groupAlertBehavior) { Notification notif = new Notification.Builder(mContext, TEST_CHANNEL_ID) .setContentTitle("Title") .setSmallIcon(R.drawable.ic_person) @@ -79,7 +79,7 @@ public final class NotificationGroupTestHelper { new UserHandle(ActivityManager.getCurrentUser()), null /* overrideGroupKey */, 0 /* postTime */); - NotificationData.Entry entry = new NotificationData.Entry(sbn); + NotificationEntry entry = new NotificationEntry(sbn); ExpandableNotificationRow row = mock(ExpandableNotificationRow.class); entry.setRow(row); when(row.getEntry()).thenReturn(entry); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index 2bbfd7d8bb8d..12cb99575fb6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -40,7 +40,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.NotificationAlertingManager; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; @@ -82,7 +82,7 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { Notification n = new Notification.Builder(getContext(), "a").build(); StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n, UserHandle.of(0), null, 0); - NotificationData.Entry entry = new NotificationData.Entry(sbn); + NotificationEntry entry = new NotificationEntry(sbn); mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0, false /* animate */); TestableLooper.get(this).processAllMessages(); @@ -96,7 +96,7 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { Notification n = new Notification.Builder(getContext(), "a").build(); StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n, UserHandle.of(0), null, 0); - NotificationData.Entry entry = new NotificationData.Entry(sbn); + NotificationEntry entry = new NotificationEntry(sbn); mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */); TestableLooper.get(this).processAllMessages(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index a45a5c4c3a0e..0d4046b6147b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -95,13 +95,13 @@ import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.NotificationAlertingManager; -import com.android.systemui.statusbar.notification.NotificationData; -import com.android.systemui.statusbar.notification.NotificationData.Entry; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationFilter; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; @@ -135,7 +135,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private IDreamManager mDreamManager; @Mock private ScrimController mScrimController; @Mock private DozeScrimController mDozeScrimController; - @Mock private ArrayList<Entry> mNotificationList; + @Mock private ArrayList<NotificationEntry> mNotificationList; @Mock private BiometricUnlockController mBiometricUnlockController; @Mock private NotificationData mNotificationData; @Mock @@ -409,7 +409,7 @@ public class StatusBarTest extends SysuiTestCase { .build(); StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n, UserHandle.of(0), null, 0); - NotificationData.Entry entry = new NotificationData.Entry(sbn); + NotificationEntry entry = new NotificationEntry(sbn); entry.importance = IMPORTANCE_HIGH; assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry)); @@ -430,7 +430,7 @@ public class StatusBarTest extends SysuiTestCase { .build(); StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n, UserHandle.of(0), null, 0); - NotificationData.Entry entry = new NotificationData.Entry(sbn); + NotificationEntry entry = new NotificationEntry(sbn); entry.importance = IMPORTANCE_HIGH; assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry)); @@ -447,7 +447,7 @@ public class StatusBarTest extends SysuiTestCase { Notification n = new Notification.Builder(getContext(), "a").build(); StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n, UserHandle.of(0), null, 0); - NotificationData.Entry entry = new NotificationData.Entry(sbn); + NotificationEntry entry = new NotificationEntry(sbn); entry.suppressedVisualEffects = SUPPRESSED_EFFECT_PEEK; entry.importance = IMPORTANCE_HIGH; @@ -465,7 +465,7 @@ public class StatusBarTest extends SysuiTestCase { Notification n = new Notification.Builder(getContext(), "a").build(); StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n, UserHandle.of(0), null, 0); - NotificationData.Entry entry = new NotificationData.Entry(sbn); + NotificationEntry entry = new NotificationEntry(sbn); entry.importance = IMPORTANCE_HIGH; assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java index c5bac9242b8b..fdc8ef12c6b0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java @@ -50,7 +50,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.statusbar.SmartReplyController; -import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import com.android.systemui.statusbar.phone.ShadeController; @@ -94,7 +94,7 @@ public class SmartReplyViewTest extends SysuiTestCase { private int mSpacing; @Mock private SmartReplyController mLogger; - private NotificationData.Entry mEntry; + private NotificationEntry mEntry; private Notification mNotification; @Mock ActivityStarter mActivityStarter; @@ -127,7 +127,7 @@ public class SmartReplyViewTest extends SysuiTestCase { StatusBarNotification sbn = mock(StatusBarNotification.class); when(sbn.getNotification()).thenReturn(mNotification); when(sbn.getKey()).thenReturn(TEST_NOTIFICATION_KEY); - mEntry = new NotificationData.Entry(sbn); + mEntry = new NotificationEntry(sbn); mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person); } diff --git a/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp b/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp index 846ff2517d12..a40cf1c06d07 100644 --- a/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp +++ b/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp @@ -27,23 +27,7 @@ namespace android { -class ProxyErrorLogger : public net::ProxyErrorListener { -public: - ~ProxyErrorLogger() { - - } - void AlertMessage(String16 message) { - String8 str(message); - ALOGD("Alert: %s", str.string()); - } - void ErrorMessage(String16 message) { - String8 str(message); - ALOGE("Error: %s", str.string()); - } -}; - net::ProxyResolverV8* proxyResolver = NULL; -ProxyErrorLogger* logger = NULL; bool pacSet = false; String16 jstringToString16(JNIEnv* env, jstring jstr) { @@ -64,9 +48,7 @@ jstring string16ToJstring(JNIEnv* env, String16 string) { static jboolean com_android_pacprocessor_PacNative_createV8ParserNativeLocked(JNIEnv* /* env */, jobject) { if (proxyResolver == NULL) { - logger = new ProxyErrorLogger(); - proxyResolver = new net::ProxyResolverV8(net::ProxyResolverJSBindings::CreateDefault(), - logger); + proxyResolver = new net::ProxyResolverV8(net::ProxyResolverJSBindings::CreateDefault()); pacSet = false; return JNI_FALSE; } @@ -76,9 +58,7 @@ static jboolean com_android_pacprocessor_PacNative_createV8ParserNativeLocked(JN static jboolean com_android_pacprocessor_PacNative_destroyV8ParserNativeLocked(JNIEnv* /* env */, jobject) { if (proxyResolver != NULL) { - delete logger; delete proxyResolver; - logger = NULL; proxyResolver = NULL; return JNI_FALSE; } diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index 3379b1b6d15a..2b399defc21b 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -3684,7 +3684,7 @@ message MetricsEvent { ACTION_SETTINGS_TILE_CLICK = 830; // OPEN: Notification unsnoozed. CLOSE: Notification snoozed. UPDATE: snoozed notification - // updated + // updated. TYPE_DISMISS: snoozing canceled due to data being cleared on package // CATEGORY: NOTIFICATION // OS: O NOTIFICATION_SNOOZED = 831; diff --git a/services/Android.bp b/services/Android.bp index 58a09977596f..01734f4051d3 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -28,6 +28,7 @@ java_library { "services.net", "services.print", "services.restrictions", + "services.startop", "services.usage", "services.usb", "services.voiceinteraction", diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index 1a6bee9daaf3..6cccd62d563f 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -70,6 +70,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.IResultReceiver; import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; +import com.android.internal.util.SyncResultReceiver; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.autofill.ui.AutoFillUI; @@ -606,15 +607,15 @@ public final class AutofillManagerService } private void send(@NonNull IResultReceiver receiver, @Nullable String value) { - send(receiver, AutofillManager.SyncResultReceiver.bundleFor(value)); + send(receiver, SyncResultReceiver.bundleFor(value)); } private void send(@NonNull IResultReceiver receiver, @Nullable String[] value) { - send(receiver, AutofillManager.SyncResultReceiver.bundleFor(value)); + send(receiver, SyncResultReceiver.bundleFor(value)); } private void send(@NonNull IResultReceiver receiver, @Nullable Parcelable value) { - send(receiver, AutofillManager.SyncResultReceiver.bundleFor(value)); + send(receiver, SyncResultReceiver.bundleFor(value)); } private void send(@NonNull IResultReceiver receiver, boolean value) { diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 4b7efe1dfa71..a6bb049602a0 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -870,8 +870,11 @@ final class AutofillManagerServiceImpl } pw.print(prefix); pw.print("Default component: "); pw.println(getContext() .getString(R.string.config_defaultAutofillService)); - pw.print(prefix); pw.print("mAugmentedAutofillNamer: "); - mAugmentedAutofillResolver.dumpShort(pw); pw.println(); + + pw.print(prefix); pw.println("mAugmentedAutofillNamer: "); + pw.print(prefix2); mAugmentedAutofillResolver.dumpShort(pw); pw.println(); + pw.print(prefix2); mAugmentedAutofillResolver.dumpShort(pw, mUserId); pw.println(); + if (mRemoteAugmentedAutofillService != null) { pw.print(prefix); pw.println("RemoteAugmentedAutofillService: "); mRemoteAugmentedAutofillService.dump(prefix2, pw); diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 6bd399042b29..12db4f37da58 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -39,7 +39,6 @@ import android.os.Binder; import android.os.HandlerThread; import android.os.IBinder; import android.os.ParcelFileDescriptor; -import android.os.RemoteException; import android.os.Trace; import android.os.UserHandle; import android.util.Slog; @@ -139,13 +138,7 @@ public class BackupManagerService { mServiceUsers.put(userId, userBackupManagerService); Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable"); - try { - // TODO(b/121198604): Make enable file per-user and clean up indirection. - mTrampoline.setBackupEnabledForUser( - userId, UserBackupManagerFilePersistedSettings.readBackupEnableState(userId)); - } catch (RemoteException e) { - // Can't happen, it's a local object. - } + userBackupManagerService.initializeBackupEnableState(); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index 725734a43946..b01117c438f0 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -560,6 +560,11 @@ public class UserBackupManagerService { mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*"); } + void initializeBackupEnableState() { + boolean isEnabled = UserBackupManagerFilePersistedSettings.readBackupEnableState(mUserId); + setBackupEnabled(isEnabled); + } + /** Cleans up state when the user of this service is stopped. */ void tearDownService() { mUserBackupThread.quit(); diff --git a/services/backup/java/com/android/server/backup/internal/SetupObserver.java b/services/backup/java/com/android/server/backup/internal/SetupObserver.java index defcacc50a18..62ae3eb8e192 100644 --- a/services/backup/java/com/android/server/backup/internal/SetupObserver.java +++ b/services/backup/java/com/android/server/backup/internal/SetupObserver.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2019 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. diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java index c467935f1996..09aa4212654e 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -95,7 +95,7 @@ final class ContentCapturePerUserService final ComponentName serviceComponentName = updateServiceInfoLocked(); if (serviceComponentName == null) { - Slog.w(TAG, "updateRemoteService(): no service componennt name"); + if (mMaster.debug) Slog.d(TAG, "updateRemoteService(): no service component name"); return; } @@ -163,7 +163,10 @@ final class ContentCapturePerUserService @NonNull String sessionId, int uid, int flags, @NonNull IResultReceiver clientReceiver) { if (!isEnabledLocked()) { - setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED, /* binder=*/ null); + // TODO: it would be better to split in differet reasons, like + // STATE_DISABLED_NO_SERVICE and STATE_DISABLED_BY_DEVICE_POLICY + setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_NO_SERVICE, + /* binder= */ null); return; } final ComponentName serviceComponentName = getServiceComponentName(); @@ -195,8 +198,8 @@ final class ContentCapturePerUserService Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken + ": ignoring because service is not set"); // TODO(b/111276913): use a new disabled state? - setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED, - /* binder=*/ null); + setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_NO_SERVICE, + /* binder= */ null); return; } @@ -285,7 +288,7 @@ final class ContentCapturePerUserService final int numSessions = mSessions.size(); for (int i = 0; i < numSessions; i++) { final ContentCaptureServerSession session = mSessions.valueAt(i); - session.destroyLocked(true); + session.destroyLocked(/* notifyRemoteService= */ true); } mSessions.clear(); } diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 2346cfc07a83..5cea56ebf2fc 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -64,7 +64,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; -import android.os.Message; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; @@ -87,6 +86,7 @@ import com.android.internal.location.ProviderRequest; import com.android.internal.os.BackgroundThread; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; +import com.android.internal.util.Preconditions; import com.android.server.location.AbstractLocationProvider; import com.android.server.location.ActivityRecognitionProxy; import com.android.server.location.GeocoderProxy; @@ -117,6 +117,11 @@ import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; /** * The service class that manages LocationProviders and issues location @@ -171,10 +176,7 @@ public class LocationManagerService extends ILocationManager.Stub { private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest(); private final Context mContext; - private final AppOpsManager mAppOps; - - // used internally for synchronization - private final Object mLock = new Object(); + private AppOpsManager mAppOps; // --- fields below are final after systemRunning() --- private LocationFudger mLocationFudger; @@ -186,7 +188,7 @@ public class LocationManagerService extends ILocationManager.Stub { private GeocoderProxy mGeocodeProvider; private GnssStatusListenerHelper mGnssStatusProvider; private INetInitiatedListener mNetInitiatedListener; - private LocationWorkerHandler mLocationHandler; + private final Handler mHandler; private PassiveProvider mPassiveProvider; // track passive provider for special cases private LocationBlacklist mBlacklist; private GnssMeasurementsProvider mGnssMeasurementsProvider; @@ -195,8 +197,6 @@ public class LocationManagerService extends ILocationManager.Stub { private boolean mLocationControllerExtraPackageEnabled; private IGpsGeofenceHardware mGpsGeofenceProxy; - // --- fields below are protected by mLock --- - // Mock (test) providers private final HashMap<String, MockProvider> mMockProviders = new HashMap<>(); @@ -205,8 +205,8 @@ public class LocationManagerService extends ILocationManager.Stub { private final HashMap<Object, Receiver> mReceivers = new HashMap<>(); // currently installed providers (with mocks replacing real providers) - private final ArrayList<LocationProvider> mProviders = - new ArrayList<>(); + private final CopyOnWriteArrayList<LocationProvider> mProviders = + new CopyOnWriteArrayList<>(); // real providers, saved here when mocked out private final HashMap<String, LocationProvider> mRealProviders = @@ -232,8 +232,8 @@ public class LocationManagerService extends ILocationManager.Stub { // all providers that operate over proxy, for authorizing incoming location and whitelisting // throttling - private final ArrayList<LocationProviderProxy> mProxyProviders = - new ArrayList<>(); + private final CopyOnWriteArrayList<LocationProviderProxy> mProxyProviders = + new CopyOnWriteArrayList<>(); private final ArraySet<String> mBackgroundThrottlePackageWhitelist = new ArraySet<>(); @@ -246,9 +246,6 @@ public class LocationManagerService extends ILocationManager.Stub { private int mCurrentUserId = UserHandle.USER_SYSTEM; private int[] mCurrentUserProfiles = new int[]{UserHandle.USER_SYSTEM}; - // Maximum age of last location returned to clients with foreground-only location permissions. - private long mLastLocationMaxAgeMs; - private GnssLocationProvider.GnssSystemInfoProvider mGnssSystemInfoProvider; private GnssLocationProvider.GnssMetricsProvider mGnssMetricsProvider; @@ -261,7 +258,7 @@ public class LocationManagerService extends ILocationManager.Stub { public LocationManagerService(Context context) { super(); mContext = context; - mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); + mHandler = BackgroundThread.getHandler(); // Let the package manager query which are the default location // providers as they get certain permissions granted by default. @@ -271,132 +268,92 @@ public class LocationManagerService extends ILocationManager.Stub { userId -> mContext.getResources().getStringArray( com.android.internal.R.array.config_locationProviderPackageNames)); - if (D) Log.d(TAG, "Constructed"); - // most startup is deferred until systemRunning() } public void systemRunning() { - synchronized (mLock) { - if (D) Log.d(TAG, "systemRunning()"); - - // fetch package manager - mPackageManager = mContext.getPackageManager(); - - // fetch power manager - mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - - // fetch activity manager - mActivityManager - = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); - - // prepare worker thread - mLocationHandler = new LocationWorkerHandler(BackgroundThread.get().getLooper()); - - // prepare mLocationHandler's dependents - mLocationFudger = new LocationFudger(mContext, mLocationHandler); - mBlacklist = new LocationBlacklist(mContext, mLocationHandler); - mBlacklist.init(); - mGeofenceManager = new GeofenceManager(mContext, mBlacklist); - - // Monitor for app ops mode changes. - AppOpsManager.OnOpChangedListener callback - = new AppOpsManager.OnOpChangedInternalListener() { - public void onOpChanged(int op, String packageName) { - mLocationHandler.post(() -> { - synchronized (mLock) { - for (Receiver receiver : mReceivers.values()) { - receiver.updateMonitoring(true); - } - applyAllProviderRequirementsLocked(); - } - }); - } - }; - mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null, - AppOpsManager.WATCH_FOREGROUND_CHANGES, callback); + runInternal(this::initialize); + } + + private void initialize() { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); + + mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); + mPackageManager = mContext.getPackageManager(); + mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); + + // prepare mHandler's dependents + mLocationFudger = new LocationFudger(mContext, mHandler); + mBlacklist = new LocationBlacklist(mContext, mHandler); + mBlacklist.init(); + mGeofenceManager = new GeofenceManager(mContext, mBlacklist); + + mAppOps.startWatchingMode( + AppOpsManager.OP_COARSE_LOCATION, + null, + AppOpsManager.WATCH_FOREGROUND_CHANGES, + new AppOpsManager.OnOpChangedInternalListener() { + public void onOpChanged(int op, String packageName) { + mHandler.post(() -> onAppOpChanged()); + } + }); - PackageManager.OnPermissionsChangedListener permissionListener = uid -> { - synchronized (mLock) { - applyAllProviderRequirementsLocked(); - } - }; - mPackageManager.addOnPermissionsChangeListener(permissionListener); + mPackageManager.addOnPermissionsChangeListener( + uid -> runInternal(this::onPermissionsChanged)); - // listen for background/foreground changes - ActivityManager.OnUidImportanceListener uidImportanceListener = - (uid, importance) -> mLocationHandler.post( - () -> onUidImportanceChanged(uid, importance)); - mActivityManager.addOnUidImportanceListener(uidImportanceListener, - FOREGROUND_IMPORTANCE_CUTOFF); + mActivityManager.addOnUidImportanceListener( + (uid, importance) -> mHandler.post( + () -> onUidImportanceChanged(uid, importance)), + FOREGROUND_IMPORTANCE_CUTOFF); - mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - updateUserProfiles(mCurrentUserId); + mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + updateUserProfiles(mCurrentUserId); - updateBackgroundThrottlingWhitelistLocked(); - updateLastLocationMaxAgeLocked(); + updateBackgroundThrottlingWhitelist(); - // prepare providers - loadProvidersLocked(); - updateProvidersSettingsLocked(); - for (LocationProvider provider : mProviders) { - applyRequirementsLocked(provider.getName()); - } + // prepare providers + loadProvidersLocked(); + updateProvidersSettings(); + for (LocationProvider provider : mProviders) { + applyRequirements(provider.getName()); } // listen for settings changes mContext.getContentResolver().registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.LOCATION_PROVIDERS_ALLOWED), true, - new ContentObserver(mLocationHandler) { + new ContentObserver(mHandler) { @Override public void onChange(boolean selfChange) { - synchronized (mLock) { - updateProvidersSettingsLocked(); - } + onProviderAllowedChanged(); } }, UserHandle.USER_ALL); mContext.getContentResolver().registerContentObserver( Settings.Global.getUriFor(Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS), true, - new ContentObserver(mLocationHandler) { + new ContentObserver(mHandler) { @Override public void onChange(boolean selfChange) { - synchronized (mLock) { - for (LocationProvider provider : mProviders) { - applyRequirementsLocked(provider.getName()); - } - } + onBackgroundThrottleIntervalChanged(); } }, UserHandle.USER_ALL); mContext.getContentResolver().registerContentObserver( - Settings.Global.getUriFor(Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS), - true, - new ContentObserver(mLocationHandler) { - @Override - public void onChange(boolean selfChange) { - synchronized (mLock) { - updateLastLocationMaxAgeLocked(); - } - } - } - ); - mContext.getContentResolver().registerContentObserver( Settings.Global.getUriFor( Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST), true, - new ContentObserver(mLocationHandler) { + new ContentObserver(mHandler) { @Override public void onChange(boolean selfChange) { - synchronized (mLock) { - updateBackgroundThrottlingWhitelistLocked(); - for (LocationProvider provider : mProviders) { - applyRequirementsLocked(provider.getName()); - } - } + onBackgroundThrottleWhitelistChanged(); } }, UserHandle.USER_ALL); - mPackageMonitor.register(mContext, mLocationHandler.getLooper(), true); + new PackageMonitor() { + @Override + public void onPackageDisappeared(String packageName, int reason) { + LocationManagerService.this.onPackageDisappeared(packageName); + } + }.register(mContext, mHandler.getLooper(), true); // listen for user change IntentFilter intentFilter = new IntentFilter(); @@ -415,73 +372,170 @@ public class LocationManagerService extends ILocationManager.Stub { updateUserProfiles(mCurrentUserId); } } - }, UserHandle.ALL, intentFilter, null, mLocationHandler); + }, UserHandle.ALL, intentFilter, null, mHandler); } - private void onUidImportanceChanged(int uid, int importance) { - boolean foreground = isImportanceForeground(importance); - HashSet<String> affectedProviders = new HashSet<>(mRecordsByProvider.size()); - synchronized (mLock) { - for (Entry<String, ArrayList<UpdateRecord>> entry - : mRecordsByProvider.entrySet()) { - String provider = entry.getKey(); - for (UpdateRecord record : entry.getValue()) { - if (record.mReceiver.mIdentity.mUid == uid - && record.mIsForegroundUid != foreground) { - if (D) { - Log.d(TAG, "request from uid " + uid + " is now " - + (foreground ? "foreground" : "background)")); - } - record.updateForeground(foreground); + // will block until completion and propagate exceptions, and thus should be used from binder + // threads, particuarily binder threads from components that sit above LMS (ie, not location + // providers). + private void runFromBinderBlocking(Runnable runnable) throws RemoteException { + runFromBinderBlocking(Executors.callable(runnable)); + } - if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) { - affectedProviders.add(provider); - } - } + // will block until completion and propagate exceptions, and thus should be used from binder + // threads, particuarily binder threads from components that sit above LMS (ie, not location + // providers). + private <T> T runFromBinderBlocking(Callable<T> callable) throws RemoteException { + FutureTask<T> task = new FutureTask<>(callable); + long identity = Binder.clearCallingIdentity(); + try { + runInternal(task); + } finally { + Binder.restoreCallingIdentity(identity); + } + try { + return task.get(); + } catch (ExecutionException e) { + // binder calls can handle 3 types of exceptions, runtimeexception and error (which can + // be thrown any time), and remote exception. we transfer all of these exceptions from + // the execution thread to the binder thread so that they may propagate normally. note + // that we are loosing some context in doing so (losing the stack trace from the binder + // thread). + if (e.getCause() instanceof RemoteException) { + throw (RemoteException) e.getCause(); + } else if (e.getCause() instanceof RuntimeException) { + throw (RuntimeException) e.getCause(); + } else if (e.getCause() instanceof Error) { + throw (Error) e.getCause(); + } + + // callers should not throw checked exceptions + Log.wtf(TAG, "caller threw checked exception", e); + throw new UnsupportedOperationException(e); + } catch (InterruptedException e) { + throw new RemoteException("Binder call interrupted", e, true, true); + } + } + + // will return immediately and will not propagate exceptions. should be used for non-binder work + // that needs to be shifted onto the location thread, primarily listeners that do not support + // running on arbitrary threads. + private void runInternal(Runnable runnable) { + // it would be a better use of resources to use locks to manage cross thread access to + // various pieces on information. however, the history of the location package has mostly + // shown that this is difficult to maintain in a multi-dev environment, and tends to always + // lead towards the use of uber-locks and deadlocks. using a single thread to run everything + // is more understandable for most devs, and seems less likely to result in future bugs + if (Looper.myLooper() == mHandler.getLooper()) { + runnable.run(); + } else { + mHandler.post(runnable); + } + } + + private void onAppOpChanged() { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); + for (Receiver receiver : mReceivers.values()) { + receiver.updateMonitoring(true); + } + for (LocationProvider p : mProviders) { + applyRequirements(p.getName()); + } + } + + private void onPermissionsChanged() { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); + for (LocationProvider p : mProviders) { + applyRequirements(p.getName()); + } + } + + private void onProviderAllowedChanged() { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); + updateProvidersSettings(); + } + + private void onPackageDisappeared(String packageName) { + ArrayList<Receiver> deadReceivers = null; + + for (Receiver receiver : mReceivers.values()) { + if (receiver.mIdentity.mPackageName.equals(packageName)) { + if (deadReceivers == null) { + deadReceivers = new ArrayList<>(); } + deadReceivers.add(receiver); } - for (String provider : affectedProviders) { - applyRequirementsLocked(provider); + } + + // perform removal outside of mReceivers loop + if (deadReceivers != null) { + for (Receiver receiver : deadReceivers) { + removeUpdates(receiver); } + } + } - for (Entry<IBinder, Identity> entry : mGnssMeasurementsListeners.entrySet()) { - Identity callerIdentity = entry.getValue(); - if (callerIdentity.mUid == uid) { + private void onUidImportanceChanged(int uid, int importance) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); + boolean foreground = isImportanceForeground(importance); + HashSet<String> affectedProviders = new HashSet<>(mRecordsByProvider.size()); + for (Entry<String, ArrayList<UpdateRecord>> entry + : mRecordsByProvider.entrySet()) { + String provider = entry.getKey(); + for (UpdateRecord record : entry.getValue()) { + if (record.mReceiver.mIdentity.mUid == uid + && record.mIsForegroundUid != foreground) { if (D) { - Log.d(TAG, "gnss measurements listener from uid " + uid - + " is now " + (foreground ? "foreground" : "background)")); + Log.d(TAG, "request from uid " + uid + " is now " + + (foreground ? "foreground" : "background)")); } - if (foreground || isThrottlingExemptLocked(entry.getValue())) { - mGnssMeasurementsProvider.addListener( - IGnssMeasurementsListener.Stub.asInterface(entry.getKey()), - callerIdentity.mUid, callerIdentity.mPackageName); - } else { - mGnssMeasurementsProvider.removeListener( - IGnssMeasurementsListener.Stub.asInterface(entry.getKey())); + record.updateForeground(foreground); + + if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) { + affectedProviders.add(provider); } } } + } + for (String provider : affectedProviders) { + applyRequirements(provider); + } - for (Entry<IBinder, Identity> entry : mGnssNavigationMessageListeners.entrySet()) { - Identity callerIdentity = entry.getValue(); - if (callerIdentity.mUid == uid) { - if (D) { - Log.d(TAG, "gnss navigation message listener from uid " - + uid + " is now " - + (foreground ? "foreground" : "background)")); - } - if (foreground || isThrottlingExemptLocked(entry.getValue())) { - mGnssNavigationMessageProvider.addListener( - IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()), - callerIdentity.mUid, callerIdentity.mPackageName); - } else { - mGnssNavigationMessageProvider.removeListener( - IGnssNavigationMessageListener.Stub.asInterface(entry.getKey())); - } + for (Entry<IBinder, Identity> entry : mGnssMeasurementsListeners.entrySet()) { + Identity callerIdentity = entry.getValue(); + if (callerIdentity.mUid == uid) { + if (D) { + Log.d(TAG, "gnss measurements listener from uid " + uid + + " is now " + (foreground ? "foreground" : "background)")); + } + if (foreground || isThrottlingExemptLocked(entry.getValue())) { + mGnssMeasurementsProvider.addListener( + IGnssMeasurementsListener.Stub.asInterface(entry.getKey()), + callerIdentity.mUid, callerIdentity.mPackageName); + } else { + mGnssMeasurementsProvider.removeListener( + IGnssMeasurementsListener.Stub.asInterface(entry.getKey())); } } + } - // TODO(b/120449926): The GNSS status listeners should be handled similar to the above. + for (Entry<IBinder, Identity> entry : mGnssNavigationMessageListeners.entrySet()) { + Identity callerIdentity = entry.getValue(); + if (callerIdentity.mUid == uid) { + if (D) { + Log.d(TAG, "gnss navigation message listener from uid " + + uid + " is now " + + (foreground ? "foreground" : "background)")); + } + if (foreground || isThrottlingExemptLocked(entry.getValue())) { + mGnssNavigationMessageProvider.addListener( + IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()), + callerIdentity.mUid, callerIdentity.mPackageName); + } else { + mGnssNavigationMessageProvider.removeListener( + IGnssNavigationMessageListener.Stub.asInterface(entry.getKey())); + } + } } } @@ -489,31 +543,33 @@ public class LocationManagerService extends ILocationManager.Stub { return importance <= FOREGROUND_IMPORTANCE_CUTOFF; } - /** - * Makes a list of userids that are related to the current user. This is - * relevant when using managed profiles. Otherwise the list only contains - * the current user. - * - * @param currentUserId the current user, who might have an alter-ego. - */ - private void updateUserProfiles(int currentUserId) { - int[] profileIds = mUserManager.getProfileIdsWithDisabled(currentUserId); - synchronized (mLock) { - mCurrentUserProfiles = profileIds; + private void onBackgroundThrottleIntervalChanged() { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); + for (LocationProvider provider : mProviders) { + applyRequirements(provider.getName()); } } - /** - * Checks if the specified userId matches any of the current foreground - * users stored in mCurrentUserProfiles. - */ - private boolean isCurrentProfile(int userId) { - synchronized (mLock) { - return ArrayUtils.contains(mCurrentUserProfiles, userId); + private void onBackgroundThrottleWhitelistChanged() { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); + updateBackgroundThrottlingWhitelist(); + for (LocationProvider provider : mProviders) { + applyRequirements(provider.getName()); } } + private void updateUserProfiles(int currentUserId) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); + mCurrentUserProfiles = mUserManager.getProfileIdsWithDisabled(currentUserId); + } + + private boolean isCurrentProfile(int userId) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); + return ArrayUtils.contains(mCurrentUserProfiles, userId); + } + private void ensureFallbackFusedProviderPresentLocked(String[] pkgs) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); PackageManager pm = mContext.getPackageManager(); String systemPackageName = mContext.getPackageName(); ArrayList<HashSet<Signature>> sigSets = ServiceWatcher.getSignatureSets(mContext, pkgs); @@ -584,12 +640,13 @@ public class LocationManagerService extends ILocationManager.Stub { } private void loadProvidersLocked() { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); // create a passive location provider, which is always enabled LocationProvider passiveProviderManager = new LocationProvider( LocationManager.PASSIVE_PROVIDER); PassiveProvider passiveProvider = new PassiveProvider(passiveProviderManager); - addProviderLocked(passiveProviderManager); + addProvider(passiveProviderManager); mPassiveProvider = passiveProvider; if (GnssLocationProvider.isSupported()) { @@ -598,14 +655,14 @@ public class LocationManagerService extends ILocationManager.Stub { LocationManager.GPS_PROVIDER); GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext, gnssProviderManager, - mLocationHandler.getLooper()); + mHandler.getLooper()); mGnssSystemInfoProvider = gnssProvider.getGnssSystemInfoProvider(); mGnssBatchingProvider = gnssProvider.getGnssBatchingProvider(); mGnssMetricsProvider = gnssProvider.getGnssMetricsProvider(); mGnssStatusProvider = gnssProvider.getGnssStatusProvider(); mNetInitiatedListener = gnssProvider.getNetInitiatedListener(); - addProviderLocked(gnssProviderManager); + addProvider(gnssProviderManager); mRealProviders.put(LocationManager.GPS_PROVIDER, gnssProviderManager); mGnssMeasurementsProvider = gnssProvider.getGnssMeasurementsProvider(); mGnssNavigationMessageProvider = gnssProvider.getGnssNavigationMessageProvider(); @@ -647,7 +704,7 @@ public class LocationManagerService extends ILocationManager.Stub { if (networkProvider != null) { mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProviderManager); mProxyProviders.add(networkProvider); - addProviderLocked(networkProviderManager); + addProvider(networkProviderManager); } else { Slog.w(TAG, "no network location provider found"); } @@ -663,7 +720,7 @@ public class LocationManagerService extends ILocationManager.Stub { com.android.internal.R.string.config_fusedLocationProviderPackageName, com.android.internal.R.array.config_locationProviderPackageNames); if (fusedProvider != null) { - addProviderLocked(fusedProviderManager); + addProvider(fusedProviderManager); mProxyProviders.add(fusedProvider); mRealProviders.put(LocationManager.FUSED_PROVIDER, fusedProviderManager); } else { @@ -728,28 +785,22 @@ public class LocationManagerService extends ILocationManager.Stub { Boolean.parseBoolean(fragments[7]) /* supportsBearing */, Integer.parseInt(fragments[8]) /* powerRequirement */, Integer.parseInt(fragments[9]) /* accuracy */); - addTestProviderLocked(name, properties); + addTestProvider(name, properties); } } - /** - * Called when the device's active user changes. - * - * @param userId the new active user's UserId - */ private void switchUser(int userId) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); if (mCurrentUserId == userId) { return; } mBlacklist.switchUser(userId); - mLocationHandler.removeMessages(MSG_LOCATION_CHANGED); - synchronized (mLock) { - mLastLocation.clear(); - mLastLocationCoarseInterval.clear(); - updateUserProfiles(userId); - updateProvidersSettingsLocked(); - mCurrentUserId = userId; - } + mHandler.removeMessages(MSG_LOCATION_CHANGED); + mLastLocation.clear(); + mLastLocationCoarseInterval.clear(); + updateUserProfiles(userId); + updateProvidersSettings(); + mCurrentUserId = userId; } private static final class Identity { @@ -808,10 +859,13 @@ public class LocationManagerService extends ILocationManager.Stub { } public void setRequest(ProviderRequest request, WorkSource workSource) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); mProvider.setRequest(request, workSource); } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); + pw.println(mName + " provider:"); pw.println(" setting=" + mSettingsEnabled); pw.println(" enabled=" + mEnabled); @@ -820,34 +874,38 @@ public class LocationManagerService extends ILocationManager.Stub { } public long getStatusUpdateTime() { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); return mProvider.getStatusUpdateTime(); } public int getStatus(Bundle extras) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); return mProvider.getStatus(extras); } public void sendExtraCommand(String command, Bundle extras) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); mProvider.sendExtraCommand(command, extras); } // called from any thread @Override public void onReportLocation(Location location) { - runOnHandler(() -> LocationManagerService.this.reportLocation(location, + // no security check necessary because this is coming from an internal-only interface + runInternal(() -> LocationManagerService.this.handleLocationChanged(location, mProvider == mPassiveProvider)); } // called from any thread @Override public void onReportLocation(List<Location> locations) { - runOnHandler(() -> LocationManagerService.this.reportLocationBatch(locations)); + LocationManagerService.this.reportLocationBatch(locations); } // called from any thread @Override public void onSetEnabled(boolean enabled) { - runOnHandler(() -> { + runInternal(() -> { if (enabled == mEnabled) { return; } @@ -872,18 +930,16 @@ public class LocationManagerService extends ILocationManager.Stub { Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "-" + LocationManager.FUSED_PROVIDER, mCurrentUserId); - synchronized (mLock) { - if (!enabled) { - // If any provider has been disabled, clear all last locations for all - // providers. This is to be on the safe side in case a provider has location - // derived from this disabled provider. - mLastLocation.clear(); - mLastLocationCoarseInterval.clear(); - } - - updateProviderListenersLocked(mName); + if (!enabled) { + // If any provider has been disabled, clear all last locations for all + // providers. This is to be on the safe side in case a provider has location + // derived from this disabled provider. + mLastLocation.clear(); + mLastLocationCoarseInterval.clear(); } + updateProviderListeners(mName); + mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION), UserHandle.ALL); }); @@ -891,34 +947,27 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void onSetProperties(ProviderProperties properties) { - runOnHandler(() -> mProperties = properties); + runInternal(() -> { + mProperties = properties; + }); } private void setSettingsEnabled(boolean enabled) { - synchronized (mLock) { - if (mSettingsEnabled == enabled) { - return; - } - - mSettingsEnabled = enabled; - if (!mSettingsEnabled) { - // if any provider has been disabled, clear all last locations for all - // providers. this is to be on the safe side in case a provider has location - // derived from this disabled provider. - mLastLocation.clear(); - mLastLocationCoarseInterval.clear(); - updateProviderListenersLocked(mName); - } else if (mEnabled) { - updateProviderListenersLocked(mName); - } + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); + if (mSettingsEnabled == enabled) { + return; } - } - private void runOnHandler(Runnable runnable) { - if (Looper.myLooper() == mLocationHandler.getLooper()) { - runnable.run(); - } else { - mLocationHandler.post(runnable); + mSettingsEnabled = enabled; + if (!mSettingsEnabled) { + // if any provider has been disabled, clear all last locations for all + // providers. this is to be on the safe side in case a provider has location + // derived from this disabled provider. + mLastLocation.clear(); + mLastLocationCoarseInterval.clear(); + updateProviderListeners(mName); + } else if (mEnabled) { + updateProviderListeners(mName); } } } @@ -1022,7 +1071,7 @@ public class LocationManagerService extends ILocationManager.Stub { // See if receiver has any enabled update records. Also note if any update records // are high power (has a high power provider with an interval under a threshold). for (UpdateRecord updateRecord : mUpdateRecords.values()) { - if (isAllowedByUserSettingsLockedForUser(updateRecord.mProvider, + if (isAllowedByUserSettingsForUser(updateRecord.mProvider, mCurrentUserId)) { requestingLocation = true; LocationManagerService.LocationProvider locationProvider @@ -1122,7 +1171,7 @@ public class LocationManagerService extends ILocationManager.Stub { synchronized (this) { // synchronize to ensure incrementPendingBroadcastsLocked() // is called before decrementPendingBroadcasts() - mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler, + mPendingIntent.send(mContext, 0, statusChanged, this, mHandler, getResolutionPermission(mAllowedResolutionLevel), PendingIntentUtils.createDontSendToRestrictedAppsBundle(null)); // call this after broadcasting so we do not increment @@ -1158,7 +1207,7 @@ public class LocationManagerService extends ILocationManager.Stub { synchronized (this) { // synchronize to ensure incrementPendingBroadcastsLocked() // is called before decrementPendingBroadcasts() - mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler, + mPendingIntent.send(mContext, 0, locationChanged, this, mHandler, getResolutionPermission(mAllowedResolutionLevel), PendingIntentUtils.createDontSendToRestrictedAppsBundle(null)); // call this after broadcasting so we do not increment @@ -1201,7 +1250,7 @@ public class LocationManagerService extends ILocationManager.Stub { synchronized (this) { // synchronize to ensure incrementPendingBroadcastsLocked() // is called before decrementPendingBroadcasts() - mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler, + mPendingIntent.send(mContext, 0, providerIntent, this, mHandler, getResolutionPermission(mAllowedResolutionLevel), PendingIntentUtils.createDontSendToRestrictedAppsBundle(null)); // call this after broadcasting so we do not increment @@ -1219,12 +1268,12 @@ public class LocationManagerService extends ILocationManager.Stub { public void binderDied() { if (D) Log.d(TAG, "Location listener died"); - synchronized (mLock) { - removeUpdatesLocked(this); - } - synchronized (this) { - clearPendingBroadcastsLocked(); - } + runInternal(() -> { + removeUpdates(this); + synchronized (this) { + clearPendingBroadcastsLocked(); + } + }); } @Override @@ -1261,28 +1310,22 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public void locationCallbackFinished(ILocationListener listener) { + public void locationCallbackFinished(ILocationListener listener) throws RemoteException { //Do not use getReceiverLocked here as that will add the ILocationListener to //the receiver list if it is not found. If it is not found then the //LocationListener was removed when it had a pending broadcast and should //not be added back. - synchronized (mLock) { + runFromBinderBlocking(() -> { IBinder binder = listener.asBinder(); Receiver receiver = mReceivers.get(binder); if (receiver != null) { synchronized (receiver) { - // so wakelock calls will succeed - long identity = Binder.clearCallingIdentity(); receiver.decrementPendingBroadcastsLocked(); - Binder.restoreCallingIdentity(identity); } } - } + }); } - /** - * Returns the year of the GNSS hardware. - */ @Override public int getGnssYearOfHardware() { if (mGnssSystemInfoProvider != null) { @@ -1292,10 +1335,6 @@ public class LocationManagerService extends ILocationManager.Stub { } } - - /** - * Returns the model name of the GNSS hardware. - */ @Override @Nullable public String getGnssHardwareModelName() { @@ -1306,10 +1345,6 @@ public class LocationManagerService extends ILocationManager.Stub { } } - /** - * Runs some checks for GNSS (FINE) level permissions, used by several methods which directly - * (try to) access GNSS information at this layer. - */ private boolean hasGnssPermissions(String packageName) { int allowedResolutionLevel = getCallerAllowedResolutionLevel(); checkResolutionLevelIsSufficientForProviderUse( @@ -1329,9 +1364,6 @@ public class LocationManagerService extends ILocationManager.Stub { return hasLocationAccess; } - /** - * Returns the GNSS batching size, if available. - */ @Override public int getGnssBatchSize(String packageName) { mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, @@ -1344,10 +1376,6 @@ public class LocationManagerService extends ILocationManager.Stub { } } - /** - * Adds a callback for GNSS Batching events, if permissions allow, which are transported - * to potentially multiple listeners by the BatchedLocationCallbackTransport above this. - */ @Override public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName) { mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, @@ -1391,9 +1419,6 @@ public class LocationManagerService extends ILocationManager.Stub { } } - /** - * Removes callback for GNSS batching - */ @Override public void removeGnssBatchingCallback() { try { @@ -1408,10 +1433,6 @@ public class LocationManagerService extends ILocationManager.Stub { mGnssBatchingDeathCallback = null; } - - /** - * Starts GNSS batching, if available. - */ @Override public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName) { mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, @@ -1432,9 +1453,6 @@ public class LocationManagerService extends ILocationManager.Stub { return mGnssBatchingProvider.start(periodNanos, wakeOnFifoFull); } - /** - * Flushes a GNSS batch in progress - */ @Override public void flushGnssBatch(String packageName) { mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, @@ -1454,9 +1472,6 @@ public class LocationManagerService extends ILocationManager.Stub { } } - /** - * Stops GNSS batching - */ @Override public boolean stopGnssBatch() { mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, @@ -1475,7 +1490,7 @@ public class LocationManagerService extends ILocationManager.Stub { checkCallerIsProvider(); // Currently used only for GNSS locations - update permissions check if changed - if (isAllowedByUserSettingsLockedForUser(LocationManager.GPS_PROVIDER, mCurrentUserId)) { + if (isAllowedByUserSettingsForUser(LocationManager.GPS_PROVIDER, mCurrentUserId)) { if (mGnssBatchingCallback == null) { Slog.e(TAG, "reportLocationBatch() called without active Callback"); return; @@ -1490,35 +1505,28 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private void addProviderLocked(LocationProvider provider) { + private void addProvider(LocationProvider provider) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); mProviders.add(provider); mProvidersByName.put(provider.getName(), provider); } - private void removeProviderLocked(LocationProvider provider) { + private void removeProvider(LocationProvider provider) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); mProviders.remove(provider); mProvidersByName.remove(provider.getName()); } - /** - * Returns "true" if access to the specified location provider is allowed by the specified - * user's settings. Access to all location providers is forbidden to non-location-provider - * processes belonging to background users. - * - * @param provider the name of the location provider - * @param userId the user id to query - */ - private boolean isAllowedByUserSettingsLockedForUser(String provider, int userId) { + private boolean isAllowedByUserSettingsForUser(String provider, int userId) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); if (LocationManager.PASSIVE_PROVIDER.equals(provider)) { - return isLocationEnabledForUser(userId); + return isLocationEnabledForUserInternal(userId); } if (LocationManager.FUSED_PROVIDER.equals(provider)) { - return isLocationEnabledForUser(userId); + return isLocationEnabledForUserInternal(userId); } - synchronized (mLock) { - if (mMockProviders.containsKey(provider)) { - return isLocationEnabledForUser(userId); - } + if (mMockProviders.containsKey(provider)) { + return isLocationEnabledForUserInternal(userId); } long identity = Binder.clearCallingIdentity(); @@ -1533,29 +1541,14 @@ public class LocationManagerService extends ILocationManager.Stub { } } - - /** - * Returns "true" if access to the specified location provider is allowed by the specified - * user's settings. Access to all location providers is forbidden to non-location-provider - * processes belonging to background users. - * - * @param provider the name of the location provider - * @param uid the requestor's UID - * @param userId the user id to query - */ - private boolean isAllowedByUserSettingsLocked(String provider, int uid, int userId) { + private boolean isAllowedByUserSettings(String provider, int uid, int userId) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); if (!isCurrentProfile(UserHandle.getUserId(uid)) && !isUidALocationProvider(uid)) { return false; } - return isAllowedByUserSettingsLockedForUser(provider, userId); + return isAllowedByUserSettingsForUser(provider, userId); } - /** - * Returns the permission string associated with the specified resolution level. - * - * @param resolutionLevel the resolution level - * @return the permission string - */ private String getResolutionPermission(int resolutionLevel) { switch (resolutionLevel) { case RESOLUTION_LEVEL_FINE: @@ -1567,13 +1560,6 @@ public class LocationManagerService extends ILocationManager.Stub { } } - /** - * Returns the resolution level allowed to the given PID/UID pair. - * - * @param pid the PID - * @param uid the UID - * @return resolution level allowed to the pid/uid pair - */ private int getAllowedResolutionLevel(int pid, int uid) { if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, pid, uid) == PERMISSION_GRANTED) { @@ -1586,32 +1572,16 @@ public class LocationManagerService extends ILocationManager.Stub { } } - /** - * Returns the resolution level allowed to the caller - * - * @return resolution level allowed to caller - */ private int getCallerAllowedResolutionLevel() { return getAllowedResolutionLevel(Binder.getCallingPid(), Binder.getCallingUid()); } - /** - * Throw SecurityException if specified resolution level is insufficient to use geofences. - * - * @param allowedResolutionLevel resolution level allowed to caller - */ private void checkResolutionLevelIsSufficientForGeofenceUse(int allowedResolutionLevel) { if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) { throw new SecurityException("Geofence usage requires ACCESS_FINE_LOCATION permission"); } } - /** - * Return the minimum resolution level required to use the specified location provider. - * - * @param provider the name of the location provider - * @return minimum resolution level required for provider - */ private int getMinimumResolutionLevelForProviderUse(String provider) { if (LocationManager.GPS_PROVIDER.equals(provider) || LocationManager.PASSIVE_PROVIDER.equals(provider)) { @@ -1643,13 +1613,6 @@ public class LocationManagerService extends ILocationManager.Stub { return RESOLUTION_LEVEL_FINE; // if in doubt, require FINE } - /** - * Throw SecurityException if specified resolution level is insufficient to use the named - * location provider. - * - * @param allowedResolutionLevel resolution level allowed to caller - * @param providerName the name of the location provider - */ private void checkResolutionLevelIsSufficientForProviderUse(int allowedResolutionLevel, String providerName) { int requiredResolutionLevel = getMinimumResolutionLevelForProviderUse(providerName); @@ -1668,20 +1631,6 @@ public class LocationManagerService extends ILocationManager.Stub { } } - /** - * Throw SecurityException if WorkSource use is not allowed (i.e. can't blame other packages - * for battery). - */ - private void checkDeviceStatsAllowed() { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.UPDATE_DEVICE_STATS, null); - } - - private void checkUpdateAppOpsAllowed() { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.UPDATE_APP_OPS_STATS, null); - } - public static int resolutionLevelToOp(int allowedResolutionLevel) { if (allowedResolutionLevel != RESOLUTION_LEVEL_NONE) { if (allowedResolutionLevel == RESOLUTION_LEVEL_COARSE) { @@ -1739,19 +1688,15 @@ public class LocationManagerService extends ILocationManager.Stub { */ @Override public List<String> getAllProviders() { - ArrayList<String> out; - synchronized (mLock) { - out = new ArrayList<>(mProviders.size()); - for (LocationProvider provider : mProviders) { - String name = provider.getName(); - if (LocationManager.FUSED_PROVIDER.equals(name)) { - continue; - } - out.add(name); + ArrayList<String> providers = new ArrayList<>(mProviders.size()); + for (LocationProvider provider : mProviders) { + String name = provider.getName(); + if (LocationManager.FUSED_PROVIDER.equals(name)) { + continue; } + providers.add(name); } - if (D) Log.d(TAG, "getAllProviders()=" + out); - return out; + return providers; } /** @@ -1760,39 +1705,33 @@ public class LocationManagerService extends ILocationManager.Stub { * Can return passive provider, but never returns fused provider. */ @Override - public List<String> getProviders(Criteria criteria, boolean enabledOnly) { + public List<String> getProviders(Criteria criteria, boolean enabledOnly) + throws RemoteException { int allowedResolutionLevel = getCallerAllowedResolutionLevel(); - ArrayList<String> out; int uid = Binder.getCallingUid(); - long identity = Binder.clearCallingIdentity(); - try { - synchronized (mLock) { - out = new ArrayList<>(mProviders.size()); - for (LocationProvider provider : mProviders) { - String name = provider.getName(); - if (LocationManager.FUSED_PROVIDER.equals(name)) { - continue; - } - if (allowedResolutionLevel >= getMinimumResolutionLevelForProviderUse(name)) { - if (enabledOnly - && !isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) { - continue; - } - if (criteria != null - && !android.location.LocationProvider.propertiesMeetCriteria( - name, provider.getProperties(), criteria)) { - continue; - } - out.add(name); - } + return runFromBinderBlocking(() -> { + ArrayList<String> providers = new ArrayList<>(mProviders.size()); + for (LocationProvider provider : mProviders) { + String name = provider.getName(); + if (LocationManager.FUSED_PROVIDER.equals(name)) { + continue; + } + if (allowedResolutionLevel < getMinimumResolutionLevelForProviderUse(name)) { + continue; + } + if (enabledOnly + && !isAllowedByUserSettings(name, uid, mCurrentUserId)) { + continue; + } + if (criteria != null + && !android.location.LocationProvider.propertiesMeetCriteria( + name, provider.getProperties(), criteria)) { + continue; } + providers.add(name); } - } finally { - Binder.restoreCallingIdentity(identity); - } - - if (D) Log.d(TAG, "getProviders()=" + out); - return out; + return providers; + }); } /** @@ -1803,59 +1742,51 @@ public class LocationManagerService extends ILocationManager.Stub { * some simplified logic. */ @Override - public String getBestProvider(Criteria criteria, boolean enabledOnly) { - String result; - + public String getBestProvider(Criteria criteria, boolean enabledOnly) throws RemoteException { List<String> providers = getProviders(criteria, enabledOnly); - if (!providers.isEmpty()) { - result = pickBest(providers); - if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result); - return result; + if (providers.isEmpty()) { + providers = getProviders(null, enabledOnly); } - providers = getProviders(null, enabledOnly); + if (!providers.isEmpty()) { - result = pickBest(providers); - if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result); - return result; + if (providers.contains(LocationManager.GPS_PROVIDER)) { + return LocationManager.GPS_PROVIDER; + } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) { + return LocationManager.NETWORK_PROVIDER; + } else { + return providers.get(0); + } } - if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + null); return null; } - private String pickBest(List<String> providers) { - if (providers.contains(LocationManager.GPS_PROVIDER)) { - return LocationManager.GPS_PROVIDER; - } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) { - return LocationManager.NETWORK_PROVIDER; - } else { - return providers.get(0); - } - } - @Override - public boolean providerMeetsCriteria(String provider, Criteria criteria) { - LocationProvider p = mProvidersByName.get(provider); - if (p == null) { - throw new IllegalArgumentException("provider=" + provider); - } + public boolean providerMeetsCriteria(String provider, Criteria criteria) + throws RemoteException { + return runFromBinderBlocking(() -> { + LocationProvider p = mProvidersByName.get(provider); + if (p == null) { + throw new IllegalArgumentException("provider=" + provider); + } - boolean result = android.location.LocationProvider.propertiesMeetCriteria( - p.getName(), p.getProperties(), criteria); - if (D) Log.d(TAG, "providerMeetsCriteria(" + provider + ", " + criteria + ")=" + result); - return result; + return android.location.LocationProvider.propertiesMeetCriteria( + p.getName(), p.getProperties(), criteria); + }); } - private void updateProvidersSettingsLocked() { + private void updateProvidersSettings() { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); for (LocationProvider p : mProviders) { - p.setSettingsEnabled(isAllowedByUserSettingsLockedForUser(p.getName(), mCurrentUserId)); + p.setSettingsEnabled(isAllowedByUserSettingsForUser(p.getName(), mCurrentUserId)); } mContext.sendBroadcastAsUser(new Intent(LocationManager.MODE_CHANGED_ACTION), UserHandle.ALL); } - private void updateProviderListenersLocked(String provider) { + private void updateProviderListeners(String provider) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); LocationProvider p = mProvidersByName.get(provider); if (p == null) return; @@ -1880,14 +1811,15 @@ public class LocationManagerService extends ILocationManager.Stub { if (deadReceivers != null) { for (int i = deadReceivers.size() - 1; i >= 0; i--) { - removeUpdatesLocked(deadReceivers.get(i)); + removeUpdates(deadReceivers.get(i)); } } - applyRequirementsLocked(provider); + applyRequirements(provider); } - private void applyRequirementsLocked(String provider) { + private void applyRequirements(String provider) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); LocationProvider p = mProvidersByName.get(provider); if (p == null) return; @@ -1993,14 +1925,13 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public String[] getBackgroundThrottlingWhitelist() { - synchronized (mLock) { - return mBackgroundThrottlePackageWhitelist.toArray( - new String[0]); - } + public String[] getBackgroundThrottlingWhitelist() throws RemoteException { + return runFromBinderBlocking( + () -> mBackgroundThrottlePackageWhitelist.toArray(new String[0])); } - private void updateBackgroundThrottlingWhitelistLocked() { + private void updateBackgroundThrottlingWhitelist() { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); String setting = Settings.Global.getString( mContext.getContentResolver(), Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST); @@ -2015,15 +1946,8 @@ public class LocationManagerService extends ILocationManager.Stub { Arrays.asList(setting.split(","))); } - private void updateLastLocationMaxAgeLocked() { - mLastLocationMaxAgeMs = - Settings.Global.getLong( - mContext.getContentResolver(), - Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS, - DEFAULT_LAST_LOCATION_MAX_AGE_MS); - } - private boolean isThrottlingExemptLocked(Identity identity) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); if (identity.mUid == Process.SYSTEM_UID) { return true; } @@ -2105,7 +2029,7 @@ public class LocationManagerService extends ILocationManager.Stub { // and also remove the Receiver if it has no more update records if (receiverRecords.size() == 0) { - removeUpdatesLocked(mReceiver); + removeUpdates(mReceiver); } } @@ -2119,8 +2043,9 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private Receiver getReceiverLocked(ILocationListener listener, int pid, int uid, + private Receiver getReceiver(ILocationListener listener, int pid, int uid, String packageName, WorkSource workSource, boolean hideFromAppOps) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); IBinder binder = listener.asBinder(); Receiver receiver = mReceivers.get(binder); if (receiver == null) { @@ -2137,8 +2062,9 @@ public class LocationManagerService extends ILocationManager.Stub { return receiver; } - private Receiver getReceiverLocked(PendingIntent intent, int pid, int uid, String packageName, + private Receiver getReceiver(PendingIntent intent, int pid, int uid, String packageName, WorkSource workSource, boolean hideFromAppOps) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); Receiver receiver = mReceivers.get(intent); if (receiver == null) { receiver = new Receiver(null, intent, pid, uid, packageName, workSource, @@ -2202,29 +2128,9 @@ public class LocationManagerService extends ILocationManager.Stub { throw new SecurityException("invalid package name: " + packageName); } - private void checkPendingIntent(PendingIntent intent) { - if (intent == null) { - throw new IllegalArgumentException("invalid pending intent: " + null); - } - } - - private Receiver checkListenerOrIntentLocked(ILocationListener listener, PendingIntent intent, - int pid, int uid, String packageName, WorkSource workSource, boolean hideFromAppOps) { - if (intent == null && listener == null) { - throw new IllegalArgumentException("need either listener or intent"); - } else if (intent != null && listener != null) { - throw new IllegalArgumentException("cannot register both listener and intent"); - } else if (intent != null) { - checkPendingIntent(intent); - return getReceiverLocked(intent, pid, uid, packageName, workSource, hideFromAppOps); - } else { - return getReceiverLocked(listener, pid, uid, packageName, workSource, hideFromAppOps); - } - } - @Override public void requestLocationUpdates(LocationRequest request, ILocationListener listener, - PendingIntent intent, String packageName) { + PendingIntent intent, String packageName) throws RemoteException { if (request == null) request = DEFAULT_LOCATION_REQUEST; checkPackageName(packageName); int allowedResolutionLevel = getCallerAllowedResolutionLevel(); @@ -2232,11 +2138,13 @@ public class LocationManagerService extends ILocationManager.Stub { request.getProvider()); WorkSource workSource = request.getWorkSource(); if (workSource != null && !workSource.isEmpty()) { - checkDeviceStatsAllowed(); + mContext.enforceCallingOrSelfPermission( + Manifest.permission.UPDATE_DEVICE_STATS, null); } boolean hideFromAppOps = request.getHideFromAppOps(); if (hideFromAppOps) { - checkUpdateAppOpsAllowed(); + mContext.enforceCallingOrSelfPermission( + Manifest.permission.UPDATE_APP_OPS_STATS, null); } boolean callerHasLocationHardwarePermission = mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE) @@ -2246,25 +2154,33 @@ public class LocationManagerService extends ILocationManager.Stub { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); - // providers may use public location API's, need to clear identity - long identity = Binder.clearCallingIdentity(); - try { - // We don't check for MODE_IGNORED here; we will do that when we go to deliver - // a location. - checkLocationAccess(pid, uid, packageName, allowedResolutionLevel); - synchronized (mLock) { - Receiver recevier = checkListenerOrIntentLocked(listener, intent, pid, uid, - packageName, workSource, hideFromAppOps); - requestLocationUpdatesLocked(sanitizedRequest, recevier, uid, packageName); - } - } finally { - Binder.restoreCallingIdentity(identity); + // We don't check for MODE_IGNORED here; we will do that when we go to deliver + // a location. + checkLocationAccess(pid, uid, packageName, allowedResolutionLevel); + + if (intent == null && listener == null) { + throw new IllegalArgumentException("need either listener or intent"); + } else if (intent != null && listener != null) { + throw new IllegalArgumentException("cannot register both listener and intent"); } + + runFromBinderBlocking(() -> { + Receiver receiver; + if (intent != null) { + receiver = getReceiver(intent, pid, uid, packageName, workSource, + hideFromAppOps); + } else { + receiver = getReceiver(listener, pid, uid, packageName, workSource, + hideFromAppOps); + } + requestLocationUpdates(sanitizedRequest, receiver, uid, packageName); + }); } - private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver, + private void requestLocationUpdates(LocationRequest request, Receiver receiver, int uid, String packageName) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); // Figure out the provider. Either its explicitly request (legacy use cases), or // use the fused provider if (request == null) request = DEFAULT_LOCATION_REQUEST; @@ -2293,7 +2209,7 @@ public class LocationManagerService extends ILocationManager.Stub { } if (provider.isEnabled()) { - applyRequirementsLocked(name); + applyRequirements(name); } else { // Notify the listener that updates are currently disabled receiver.callProviderEnabledLocked(name, false); @@ -2305,27 +2221,31 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void removeUpdates(ILocationListener listener, PendingIntent intent, - String packageName) { + String packageName) throws RemoteException { checkPackageName(packageName); - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); + int pid = Binder.getCallingPid(); + int uid = Binder.getCallingUid(); - synchronized (mLock) { - Receiver receiver = checkListenerOrIntentLocked(listener, intent, pid, uid, - packageName, null, false); + if (intent == null && listener == null) { + throw new IllegalArgumentException("need either listener or intent"); + } else if (intent != null && listener != null) { + throw new IllegalArgumentException("cannot register both listener and intent"); + } - // providers may use public location API's, need to clear identity - long identity = Binder.clearCallingIdentity(); - try { - removeUpdatesLocked(receiver); - } finally { - Binder.restoreCallingIdentity(identity); + runFromBinderBlocking(() -> { + Receiver receiver; + if (intent != null) { + receiver = getReceiver(intent, pid, uid, packageName, null, false); + } else { + receiver = getReceiver(listener, pid, uid, packageName, null, false); } - } + removeUpdates(receiver); + }); } - private void removeUpdatesLocked(Receiver receiver) { + private void removeUpdates(Receiver receiver) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); if (D) Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver))); if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) { @@ -2352,20 +2272,14 @@ public class LocationManagerService extends ILocationManager.Stub { // update provider for (String provider : providers) { - applyRequirementsLocked(provider); - } - } - - private void applyAllProviderRequirementsLocked() { - for (LocationProvider p : mProviders) { - applyRequirementsLocked(p.getName()); + applyRequirements(provider); } } @Override - public Location getLastLocation(LocationRequest request, String packageName) { - if (D) Log.d(TAG, "getLastLocation: " + request); - if (request == null) request = DEFAULT_LOCATION_REQUEST; + public Location getLastLocation(LocationRequest r, String packageName) throws RemoteException { + if (D) Log.d(TAG, "getLastLocation: " + r); + LocationRequest request = r != null ? r : DEFAULT_LOCATION_REQUEST; int allowedResolutionLevel = getCallerAllowedResolutionLevel(); checkPackageName(packageName); checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel, @@ -2391,68 +2305,60 @@ public class LocationManagerService extends ILocationManager.Stub { } return null; } + } finally { + Binder.restoreCallingIdentity(identity); + } - synchronized (mLock) { - // Figure out the provider. Either its explicitly request (deprecated API's), - // or use the fused provider - String name = request.getProvider(); - if (name == null) name = LocationManager.FUSED_PROVIDER; - LocationProvider provider = mProvidersByName.get(name); - if (provider == null) return null; + return runFromBinderBlocking(() -> { + // Figure out the provider. Either its explicitly request (deprecated API's), + // or use the fused provider + String name = request.getProvider(); + if (name == null) name = LocationManager.FUSED_PROVIDER; + LocationProvider provider = mProvidersByName.get(name); + if (provider == null) return null; - if (!isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) return null; + if (!isAllowedByUserSettings(name, uid, mCurrentUserId)) return null; - Location location; - if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) { - // Make sure that an app with coarse permissions can't get frequent location - // updates by calling LocationManager.getLastKnownLocation repeatedly. - location = mLastLocationCoarseInterval.get(name); - } else { - location = mLastLocation.get(name); - } - if (location == null) { - return null; - } + Location location; + if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) { + // Make sure that an app with coarse permissions can't get frequent location + // updates by calling LocationManager.getLastKnownLocation repeatedly. + location = mLastLocationCoarseInterval.get(name); + } else { + location = mLastLocation.get(name); + } + if (location == null) { + return null; + } - // Don't return stale location to apps with foreground-only location permission. - String op = resolutionLevelToOpStr(allowedResolutionLevel); - long locationAgeMs = SystemClock.elapsedRealtime() - - location.getElapsedRealtimeNanos() / NANOS_PER_MILLI; - if ((locationAgeMs > mLastLocationMaxAgeMs) - && (mAppOps.unsafeCheckOp(op, uid, packageName) - == AppOpsManager.MODE_FOREGROUND)) { - return null; - } + // Don't return stale location to apps with foreground-only location permission. + String op = resolutionLevelToOpStr(allowedResolutionLevel); + long locationAgeMs = SystemClock.elapsedRealtime() + - location.getElapsedRealtimeNanos() / NANOS_PER_MILLI; + if ((locationAgeMs > Settings.Global.getLong( + mContext.getContentResolver(), + Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS, + DEFAULT_LAST_LOCATION_MAX_AGE_MS)) + && (mAppOps.unsafeCheckOp(op, uid, packageName) + == AppOpsManager.MODE_FOREGROUND)) { + return null; + } - if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) { - Location noGPSLocation = location.getExtraLocation( - Location.EXTRA_NO_GPS_LOCATION); - if (noGPSLocation != null) { - return new Location(mLocationFudger.getOrCreate(noGPSLocation)); - } - } else { - return new Location(location); + if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) { + Location noGPSLocation = location.getExtraLocation( + Location.EXTRA_NO_GPS_LOCATION); + if (noGPSLocation != null) { + return new Location(mLocationFudger.getOrCreate(noGPSLocation)); } + } else { + return new Location(location); } return null; - } finally { - Binder.restoreCallingIdentity(identity); - } + }); } - /** - * Provides an interface to inject and set the last location if location is not available - * currently. - * - * This helps in cases where the product (Cars for example) has saved the last known location - * before powering off. This interface lets the client inject the saved location while the GPS - * chipset is getting its first fix, there by improving user experience. - * - * @param location - Location object to inject - * @return true if update was successful, false if not - */ @Override - public boolean injectLocation(Location location) { + public boolean injectLocation(Location location) throws RemoteException { mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, "Location Hardware permission not granted to inject location"); mContext.enforceCallingPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, @@ -2464,29 +2370,31 @@ public class LocationManagerService extends ILocationManager.Stub { } return false; } - LocationProvider p = null; - String provider = location.getProvider(); - if (provider != null) { - p = mProvidersByName.get(provider); - } - if (p == null) { - if (D) { - Log.d(TAG, "injectLocation(): unknown provider"); + + return runFromBinderBlocking(() -> { + LocationProvider p = null; + String provider = location.getProvider(); + if (provider != null) { + p = mProvidersByName.get(provider); } - return false; - } - synchronized (mLock) { - if (!isAllowedByUserSettingsLockedForUser(provider, mCurrentUserId)) { + if (p == null) { + if (D) { + Log.d(TAG, "injectLocation(): unknown provider"); + } + return false; + } + if (!isAllowedByUserSettingsForUser(provider, mCurrentUserId)) { if (D) { - Log.d(TAG, "Location disabled in Settings for current user:" + mCurrentUserId); + Log.d(TAG, "Location disabled in Settings for current user:" + + mCurrentUserId); } return false; } else { // NOTE: If last location is already available, location is not injected. If - // provider's normal source (like a GPS chipset) have already provided an output, + // provider's normal source (like a GPS chipset) have already provided an output // there is no need to inject this location. if (mLastLocation.get(provider) == null) { - updateLastLocationLocked(location, provider); + updateLastLocation(location, provider); } else { if (D) { Log.d(TAG, "injectLocation(): Location exists. Not updating"); @@ -2494,8 +2402,8 @@ public class LocationManagerService extends ILocationManager.Stub { return false; } } - } - return true; + return true; + }); } @Override @@ -2504,7 +2412,9 @@ public class LocationManagerService extends ILocationManager.Stub { if (request == null) request = DEFAULT_LOCATION_REQUEST; int allowedResolutionLevel = getCallerAllowedResolutionLevel(); checkResolutionLevelIsSufficientForGeofenceUse(allowedResolutionLevel); - checkPendingIntent(intent); + if (intent == null) { + throw new IllegalArgumentException("invalid pending intent: " + null); + } checkPackageName(packageName); checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel, request.getProvider()); @@ -2515,7 +2425,10 @@ public class LocationManagerService extends ILocationManager.Stub { LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel, callerHasLocationHardwarePermission); - if (D) Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + geofence + " " + intent); + if (D) { + Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + geofence + " " + + intent); + } // geo-fence manager uses the public location API, need to clear identity int uid = Binder.getCallingUid(); @@ -2536,7 +2449,9 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) { - checkPendingIntent(intent); + if (intent == null) { + throw new IllegalArgumentException("invalid pending intent: " + null); + } checkPackageName(packageName); if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent); @@ -2568,14 +2483,14 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean addGnssMeasurementsListener( - IGnssMeasurementsListener listener, String packageName) { + IGnssMeasurementsListener listener, String packageName) throws RemoteException { if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) { return false; } - synchronized (mLock) { - Identity callerIdentity - = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName); + Identity callerIdentity = + new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName); + return runFromBinderBlocking(() -> { // TODO(b/120481270): Register for client death notification and update map. mGnssMeasurementsListeners.put(listener.asBinder(), callerIdentity); long identity = Binder.clearCallingIdentity(); @@ -2591,7 +2506,7 @@ public class LocationManagerService extends ILocationManager.Stub { } return true; - } + }); } @Override @@ -2619,28 +2534,30 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) { + public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) + throws RemoteException { if (mGnssMeasurementsProvider == null) { return; } - synchronized (mLock) { + runFromBinderBlocking(() -> { mGnssMeasurementsListeners.remove(listener.asBinder()); mGnssMeasurementsProvider.removeListener(listener); - } + }); } @Override public boolean addGnssNavigationMessageListener( IGnssNavigationMessageListener listener, - String packageName) { + String packageName) throws RemoteException { if (!hasGnssPermissions(packageName) || mGnssNavigationMessageProvider == null) { return false; } - synchronized (mLock) { - Identity callerIdentity - = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName); + Identity callerIdentity = + new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName); + + return runFromBinderBlocking(() -> { // TODO(b/120481270): Register for client death notification and update map. mGnssNavigationMessageListeners.put(listener.asBinder(), callerIdentity); long identity = Binder.clearCallingIdentity(); @@ -2656,21 +2573,23 @@ public class LocationManagerService extends ILocationManager.Stub { } return true; - } + }); } @Override - public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) { + public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) + throws RemoteException { if (mGnssNavigationMessageProvider != null) { - synchronized (mLock) { + runFromBinderBlocking(() -> { mGnssNavigationMessageListeners.remove(listener.asBinder()); mGnssNavigationMessageProvider.removeListener(listener); - } + }); } } @Override - public boolean sendExtraCommand(String provider, String command, Bundle extras) { + public boolean sendExtraCommand(String provider, String command, Bundle extras) + throws RemoteException { if (provider == null) { // throw NullPointerException to remain compatible with previous implementation throw new NullPointerException(); @@ -2684,13 +2603,13 @@ public class LocationManagerService extends ILocationManager.Stub { throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission"); } - synchronized (mLock) { + return runFromBinderBlocking(() -> { LocationProvider p = mProvidersByName.get(provider); if (p == null) return false; p.sendExtraCommand(command, extras); return true; - } + }); } @Override @@ -2707,251 +2626,181 @@ public class LocationManagerService extends ILocationManager.Stub { } } - /** - * @return null if the provider does not exist - * @throws SecurityException if the provider is not allowed to be - * accessed by the caller - */ @Override - public ProviderProperties getProviderProperties(String provider) { + public ProviderProperties getProviderProperties(String provider) throws RemoteException { checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(), provider); - LocationProvider p; - synchronized (mLock) { - p = mProvidersByName.get(provider); - } - - if (p == null) return null; - return p.getProperties(); + return runFromBinderBlocking(() -> { + LocationProvider p = mProvidersByName.get(provider); + if (p == null) return null; + return p.getProperties(); + }); } - /** - * @return null if the provider does not exist - * @throws SecurityException if the provider is not allowed to be - * accessed by the caller - */ @Override - public String getNetworkProviderPackage() { - LocationProvider p; - synchronized (mLock) { - p = mProvidersByName.get(LocationManager.NETWORK_PROVIDER); - } + public String getNetworkProviderPackage() throws RemoteException { + return runFromBinderBlocking(() -> { + LocationProvider p = mProvidersByName.get(LocationManager.NETWORK_PROVIDER); - if (p == null) { + if (p == null) { + return null; + } + if (p.mProvider instanceof LocationProviderProxy) { + return ((LocationProviderProxy) p.mProvider).getConnectedPackageName(); + } return null; - } - if (p.mProvider instanceof LocationProviderProxy) { - return ((LocationProviderProxy) p.mProvider).getConnectedPackageName(); - } - return null; + }); } @Override - public void setLocationControllerExtraPackage(String packageName) { + public void setLocationControllerExtraPackage(String packageName) throws RemoteException { mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, Manifest.permission.LOCATION_HARDWARE + " permission required"); - synchronized (mLock) { - mLocationControllerExtraPackage = packageName; - } + + runFromBinderBlocking(() -> mLocationControllerExtraPackage = packageName); } @Override - public String getLocationControllerExtraPackage() { - synchronized (mLock) { - return mLocationControllerExtraPackage; - } + public String getLocationControllerExtraPackage() throws RemoteException { + return runFromBinderBlocking(() -> mLocationControllerExtraPackage); } @Override - public void setLocationControllerExtraPackageEnabled(boolean enabled) { + public void setLocationControllerExtraPackageEnabled(boolean enabled) throws RemoteException { mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, Manifest.permission.LOCATION_HARDWARE + " permission required"); - synchronized (mLock) { - mLocationControllerExtraPackageEnabled = enabled; - } + runFromBinderBlocking(() -> mLocationControllerExtraPackageEnabled = enabled); } @Override - public boolean isLocationControllerExtraPackageEnabled() { - synchronized (mLock) { - return mLocationControllerExtraPackageEnabled - && (mLocationControllerExtraPackage != null); - } + public boolean isLocationControllerExtraPackageEnabled() throws RemoteException { + return runFromBinderBlocking(() -> mLocationControllerExtraPackageEnabled + && (mLocationControllerExtraPackage != null)); } - /** - * Returns the current location enabled/disabled status for a user - * - * @param userId the id of the user - * @return true if location is enabled - */ @Override - public boolean isLocationEnabledForUser(int userId) { + public boolean isLocationEnabledForUser(int userId) throws RemoteException { // Check INTERACT_ACROSS_USERS permission if userId is not current user id. - checkInteractAcrossUsersPermission(userId); + if (UserHandle.getCallingUserId() != userId) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.INTERACT_ACROSS_USERS, + "Requires INTERACT_ACROSS_USERS permission"); + } - long identity = Binder.clearCallingIdentity(); - try { - synchronized (mLock) { - final String allowedProviders = Settings.Secure.getStringForUser( - mContext.getContentResolver(), - Settings.Secure.LOCATION_PROVIDERS_ALLOWED, - userId); - if (allowedProviders == null) { - return false; - } - final List<String> providerList = Arrays.asList(allowedProviders.split(",")); - for (String provider : mRealProviders.keySet()) { - if (provider.equals(LocationManager.PASSIVE_PROVIDER) - || provider.equals(LocationManager.FUSED_PROVIDER)) { - continue; - } - if (providerList.contains(provider)) { - return true; - } - } - return false; + return runFromBinderBlocking(() -> isLocationEnabledForUserInternal(userId)); + } + + private boolean isLocationEnabledForUserInternal(int userId) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); + + final String allowedProviders = Settings.Secure.getStringForUser( + mContext.getContentResolver(), + Settings.Secure.LOCATION_PROVIDERS_ALLOWED, + userId); + if (allowedProviders == null) { + return false; + } + final List<String> providerList = Arrays.asList(allowedProviders.split(",")); + + for (String provider : mRealProviders.keySet()) { + if (provider.equals(LocationManager.PASSIVE_PROVIDER) + || provider.equals(LocationManager.FUSED_PROVIDER)) { + continue; + } + if (providerList.contains(provider)) { + return true; } - } finally { - Binder.restoreCallingIdentity(identity); } + return false; } - /** - * Enable or disable location for a user - * - * @param enabled true to enable location, false to disable location - * @param userId the id of the user - */ @Override - public void setLocationEnabledForUser(boolean enabled, int userId) { + public void setLocationEnabledForUser(boolean enabled, int userId) throws RemoteException { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.WRITE_SECURE_SETTINGS, "Requires WRITE_SECURE_SETTINGS permission"); // Check INTERACT_ACROSS_USERS permission if userId is not current user id. - checkInteractAcrossUsersPermission(userId); - - long identity = Binder.clearCallingIdentity(); - try { - synchronized (mLock) { - final Set<String> allRealProviders = mRealProviders.keySet(); - // Update all providers on device plus gps and network provider when disabling - // location - Set<String> allProvidersSet = new ArraySet<>(allRealProviders.size() + 2); - allProvidersSet.addAll(allRealProviders); - // When disabling location, disable gps and network provider that could have been - // enabled by location mode api. - if (!enabled) { - allProvidersSet.add(LocationManager.GPS_PROVIDER); - allProvidersSet.add(LocationManager.NETWORK_PROVIDER); - } - if (allProvidersSet.isEmpty()) { - return; - } - // to ensure thread safety, we write the provider name with a '+' or '-' - // and let the SettingsProvider handle it rather than reading and modifying - // the list of enabled providers. - final String prefix = enabled ? "+" : "-"; - StringBuilder locationProvidersAllowed = new StringBuilder(); - for (String provider : allProvidersSet) { - if (provider.equals(LocationManager.PASSIVE_PROVIDER) - || provider.equals(LocationManager.FUSED_PROVIDER)) { - continue; - } - locationProvidersAllowed.append(prefix); - locationProvidersAllowed.append(provider); - locationProvidersAllowed.append(","); + if (UserHandle.getCallingUserId() != userId) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.INTERACT_ACROSS_USERS, + "Requires INTERACT_ACROSS_USERS permission"); + } + + runFromBinderBlocking(() -> { + final Set<String> allRealProviders = mRealProviders.keySet(); + // Update all providers on device plus gps and network provider when disabling + // location + Set<String> allProvidersSet = new ArraySet<>(allRealProviders.size() + 2); + allProvidersSet.addAll(allRealProviders); + // When disabling location, disable gps and network provider that could have been + // enabled by location mode api. + if (!enabled) { + allProvidersSet.add(LocationManager.GPS_PROVIDER); + allProvidersSet.add(LocationManager.NETWORK_PROVIDER); + } + if (allProvidersSet.isEmpty()) { + return; + } + // to ensure thread safety, we write the provider name with a '+' or '-' + // and let the SettingsProvider handle it rather than reading and modifying + // the list of enabled providers. + final String prefix = enabled ? "+" : "-"; + StringBuilder locationProvidersAllowed = new StringBuilder(); + for (String provider : allProvidersSet) { + if (provider.equals(LocationManager.PASSIVE_PROVIDER) + || provider.equals(LocationManager.FUSED_PROVIDER)) { + continue; } - // Remove the trailing comma - locationProvidersAllowed.setLength(locationProvidersAllowed.length() - 1); - Settings.Secure.putStringForUser( - mContext.getContentResolver(), - Settings.Secure.LOCATION_PROVIDERS_ALLOWED, - locationProvidersAllowed.toString(), - userId); + locationProvidersAllowed.append(prefix); + locationProvidersAllowed.append(provider); + locationProvidersAllowed.append(","); } - } finally { - Binder.restoreCallingIdentity(identity); - } + // Remove the trailing comma + locationProvidersAllowed.setLength(locationProvidersAllowed.length() - 1); + Settings.Secure.putStringForUser( + mContext.getContentResolver(), + Settings.Secure.LOCATION_PROVIDERS_ALLOWED, + locationProvidersAllowed.toString(), + userId); + }); } - /** - * Returns the current enabled/disabled status of a location provider and user - * - * @param providerName name of the provider - * @param userId the id of the user - * @return true if the provider exists and is enabled - */ @Override - public boolean isProviderEnabledForUser(String providerName, int userId) { + public boolean isProviderEnabledForUser(String providerName, int userId) + throws RemoteException { // Check INTERACT_ACROSS_USERS permission if userId is not current user id. - checkInteractAcrossUsersPermission(userId); - - if (!isLocationEnabledForUser(userId)) { - return false; + if (UserHandle.getCallingUserId() != userId) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.INTERACT_ACROSS_USERS, + "Requires INTERACT_ACROSS_USERS permission"); } // Fused provider is accessed indirectly via criteria rather than the provider-based APIs, // so we discourage its use if (LocationManager.FUSED_PROVIDER.equals(providerName)) return false; - long identity = Binder.clearCallingIdentity(); - try { - LocationProvider provider; - synchronized (mLock) { - provider = mProvidersByName.get(providerName); - } - return provider != null && provider.isEnabled(); - } finally { - Binder.restoreCallingIdentity(identity); + if (!isLocationEnabledForUser(userId)) { + return false; } + + return runFromBinderBlocking(() -> { + LocationProvider provider = mProvidersByName.get(providerName); + return provider != null && provider.isEnabled(); + }); } - /** - * Enable or disable a single location provider. - * - * @param provider name of the provider - * @param enabled true to enable the provider. False to disable the provider - * @param userId the id of the user to set - * @return true if the value was set, false on errors - */ @Override public boolean setProviderEnabledForUser(String provider, boolean enabled, int userId) { return false; } - /** - * Method for checking INTERACT_ACROSS_USERS permission if specified user id is not the same as - * current user id - * - * @param userId the user id to get or set value - */ - private void checkInteractAcrossUsersPermission(int userId) { - int uid = Binder.getCallingUid(); - if (UserHandle.getUserId(uid) != userId) { - if (ActivityManager.checkComponentPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS, uid, -1, true) - != PERMISSION_GRANTED) { - throw new SecurityException("Requires INTERACT_ACROSS_USERS permission"); - } - } - } - - /** - * Returns "true" if the UID belongs to a bound location provider. - * - * @param uid the uid - * @return true if uid belongs to a bound location provider - */ private boolean isUidALocationProvider(int uid) { if (uid == Process.SYSTEM_UID) { return true; } - if (mGeocodeProvider != null) { - if (doesUidHavePackage(uid, mGeocodeProvider.getConnectedPackageName())) return true; - } + for (LocationProviderProxy proxy : mProxyProviders) { if (doesUidHavePackage(uid, proxy.getConnectedPackageName())) return true; } @@ -2970,7 +2819,6 @@ public class LocationManagerService extends ILocationManager.Stub { // providers installed oustide the system image. So // also allow providers with a UID matching the // currently bound package name - if (isUidALocationProvider(Binder.getCallingUid())) { return; } @@ -2979,9 +2827,6 @@ public class LocationManagerService extends ILocationManager.Stub { "or UID of a currently bound location provider"); } - /** - * Returns true if the given package belongs to the given uid. - */ private boolean doesUidHavePackage(int uid, String packageName) { if (packageName == null) { return false; @@ -2998,22 +2843,12 @@ public class LocationManagerService extends ILocationManager.Stub { return false; } + // TODO: will be removed in future @Override public void reportLocation(Location location, boolean passive) { - checkCallerIsProvider(); - - if (!location.isComplete()) { - Log.w(TAG, "Dropping incomplete location: " + location); - return; - } - - mLocationHandler.removeMessages(MSG_LOCATION_CHANGED, location); - Message m = Message.obtain(mLocationHandler, MSG_LOCATION_CHANGED, location); - m.arg1 = (passive ? 1 : 0); - mLocationHandler.sendMessageAtFrontOfQueue(m); + throw new IllegalStateException("operation unsupported"); } - private static boolean shouldBroadcastSafe( Location loc, Location lastLoc, UpdateRecord record, long now) { // Always broadcast the first update @@ -3046,14 +2881,33 @@ public class LocationManagerService extends ILocationManager.Stub { return record.mRealRequest.getExpireAt() >= now; } - private void handleLocationChangedLocked(Location location, boolean passive) { - if (D) Log.d(TAG, "incoming location: " + location); + private void handleLocationChanged(Location location, boolean passive) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); + + // create a working copy of the incoming Location so that the service can modify it without + // disturbing the caller's copy + Location myLocation = new Location(location); + String pr = myLocation.getProvider(); + + // set "isFromMockProvider" bit if location came from a mock provider. we do not clear this + // bit if location did not come from a mock provider because passive/fused providers can + // forward locations from mock providers, and should not grant them legitimacy in doing so. + if (!myLocation.isFromMockProvider() && isMockProvider(pr)) { + myLocation.setIsFromMockProvider(true); + } + + if (!passive) { + // notify passive provider of the new location + mPassiveProvider.updateLocation(myLocation); + } + + if (D) Log.d(TAG, "incoming location: " + myLocation); long now = SystemClock.elapsedRealtime(); - String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider()); + String provider = (passive ? LocationManager.PASSIVE_PROVIDER : myLocation.getProvider()); // Skip if the provider is unknown. LocationProvider p = mProvidersByName.get(provider); if (p == null) return; - updateLastLocationLocked(location, provider); + updateLastLocation(myLocation, provider); // mLastLocation should have been updated from the updateLastLocationLocked call above. Location lastLocation = mLastLocation.get(provider); if (lastLocation == null) { @@ -3064,13 +2918,13 @@ public class LocationManagerService extends ILocationManager.Stub { // Update last known coarse interval location if enough time has passed. Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get(provider); if (lastLocationCoarseInterval == null) { - lastLocationCoarseInterval = new Location(location); + lastLocationCoarseInterval = new Location(myLocation); mLastLocationCoarseInterval.put(provider, lastLocationCoarseInterval); } - long timeDiffNanos = location.getElapsedRealtimeNanos() + long timeDiffNanos = myLocation.getElapsedRealtimeNanos() - lastLocationCoarseInterval.getElapsedRealtimeNanos(); if (timeDiffNanos > LocationFudger.FASTEST_INTERVAL_MS * NANOS_PER_MILLI) { - lastLocationCoarseInterval.set(location); + lastLocationCoarseInterval.set(myLocation); } // Don't ever return a coarse location that is more recent than the allowed update // interval (i.e. don't allow an app to keep registering and unregistering for @@ -3194,24 +3048,20 @@ public class LocationManagerService extends ILocationManager.Stub { // remove dead records and receivers outside the loop if (deadReceivers != null) { for (Receiver receiver : deadReceivers) { - removeUpdatesLocked(receiver); + removeUpdates(receiver); } } if (deadUpdateRecords != null) { for (UpdateRecord r : deadUpdateRecords) { r.disposeLocked(true); } - applyRequirementsLocked(provider); + applyRequirements(provider); } } - /** - * Updates last location with the given location - * - * @param location new location to update - * @param provider Location provider to update for - */ - private void updateLastLocationLocked(Location location, String provider) { + private void updateLastLocation(Location location, String provider) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); + Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION); Location lastNoGPSLocation; Location lastLocation = mLastLocation.get(provider); @@ -3229,75 +3079,11 @@ public class LocationManagerService extends ILocationManager.Stub { lastLocation.set(location); } - private class LocationWorkerHandler extends Handler { - public LocationWorkerHandler(Looper looper) { - super(looper, null, true); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_LOCATION_CHANGED: - handleLocationChanged((Location) msg.obj, msg.arg1 == 1); - break; - } - } - } - private boolean isMockProvider(String provider) { - synchronized (mLock) { - return mMockProviders.containsKey(provider); - } - } - - private void handleLocationChanged(Location location, boolean passive) { - // create a working copy of the incoming Location so that the service can modify it without - // disturbing the caller's copy - Location myLocation = new Location(location); - String provider = myLocation.getProvider(); - - // set "isFromMockProvider" bit if location came from a mock provider. we do not clear this - // bit if location did not come from a mock provider because passive/fused providers can - // forward locations from mock providers, and should not grant them legitimacy in doing so. - if (!myLocation.isFromMockProvider() && isMockProvider(provider)) { - myLocation.setIsFromMockProvider(true); - } - - synchronized (mLock) { - if (!passive) { - // notify passive provider of the new location - mPassiveProvider.updateLocation(myLocation); - } - handleLocationChangedLocked(myLocation, passive); - } + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); + return mMockProviders.containsKey(provider); } - private final PackageMonitor mPackageMonitor = new PackageMonitor() { - @Override - public void onPackageDisappeared(String packageName, int reason) { - // remove all receivers associated with this package name - synchronized (mLock) { - ArrayList<Receiver> deadReceivers = null; - - for (Receiver receiver : mReceivers.values()) { - if (receiver.mIdentity.mPackageName.equals(packageName)) { - if (deadReceivers == null) { - deadReceivers = new ArrayList<>(); - } - deadReceivers.add(receiver); - } - } - - // perform removal outside of mReceivers loop - if (deadReceivers != null) { - for (Receiver receiver : deadReceivers) { - removeUpdatesLocked(receiver); - } - } - } - } - }; - // Geocoder @Override @@ -3338,7 +3124,8 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public void addTestProvider(String name, ProviderProperties properties, String opPackageName) { + public void addTestProvider(String name, ProviderProperties properties, String opPackageName) + throws RemoteException { if (!canCallerAccessMockLocation(opPackageName)) { return; } @@ -3347,23 +3134,24 @@ public class LocationManagerService extends ILocationManager.Stub { throw new IllegalArgumentException("Cannot mock the passive location provider"); } - long identity = Binder.clearCallingIdentity(); - synchronized (mLock) { + runFromBinderBlocking(() -> { // remove the real provider if we are replacing GPS or network provider if (LocationManager.GPS_PROVIDER.equals(name) || LocationManager.NETWORK_PROVIDER.equals(name) || LocationManager.FUSED_PROVIDER.equals(name)) { LocationProvider p = mProvidersByName.get(name); if (p != null) { - removeProviderLocked(p); + removeProvider(p); } } - addTestProviderLocked(name, properties); - } - Binder.restoreCallingIdentity(identity); + addTestProvider(name, properties); + return null; + }); } - private void addTestProviderLocked(String name, ProviderProperties properties) { + private void addTestProvider(String name, ProviderProperties properties) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); + if (mProvidersByName.get(name) != null) { throw new IllegalArgumentException("Provider \"" + name + "\" already exists"); } @@ -3371,122 +3159,103 @@ public class LocationManagerService extends ILocationManager.Stub { LocationProvider provider = new LocationProvider(name); MockProvider mockProvider = new MockProvider(provider, properties); - addProviderLocked(provider); + addProvider(provider); mMockProviders.put(name, mockProvider); mLastLocation.put(name, null); mLastLocationCoarseInterval.put(name, null); } @Override - public void removeTestProvider(String provider, String opPackageName) { + public void removeTestProvider(String provider, String opPackageName) throws RemoteException { if (!canCallerAccessMockLocation(opPackageName)) { return; } - synchronized (mLock) { + runFromBinderBlocking(() -> { MockProvider mockProvider = mMockProviders.remove(provider); if (mockProvider == null) { throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); } - long identity = Binder.clearCallingIdentity(); - try { - removeProviderLocked(mProvidersByName.get(provider)); + removeProvider(mProvidersByName.get(provider)); - // reinstate real provider if available - LocationProvider realProvider = mRealProviders.get(provider); - if (realProvider != null) { - addProviderLocked(realProvider); - } - mLastLocation.put(provider, null); - mLastLocationCoarseInterval.put(provider, null); - } finally { - Binder.restoreCallingIdentity(identity); + // reinstate real provider if available + LocationProvider realProvider = mRealProviders.get(provider); + if (realProvider != null) { + addProvider(realProvider); } - } + mLastLocation.put(provider, null); + mLastLocationCoarseInterval.put(provider, null); + }); } @Override - public void setTestProviderLocation(String provider, Location loc, String opPackageName) { + public void setTestProviderLocation(String provider, Location loc, String opPackageName) + throws RemoteException { if (!canCallerAccessMockLocation(opPackageName)) { return; } - synchronized (mLock) { + runFromBinderBlocking(() -> { MockProvider mockProvider = mMockProviders.get(provider); if (mockProvider == null) { throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); } - // Ensure that the location is marked as being mock. There's some logic to do this in - // handleLocationChanged(), but it fails if loc has the wrong provider (bug 33091107). + // Ensure that the location is marked as being mock. There's some logic to do this + // in handleLocationChanged(), but it fails if loc has the wrong provider + // (b/33091107). Location mock = new Location(loc); mock.setIsFromMockProvider(true); if (!TextUtils.isEmpty(loc.getProvider()) && !provider.equals(loc.getProvider())) { - // The location has an explicit provider that is different from the mock provider - // name. The caller may be trying to fool us via bug 33091107. + // The location has an explicit provider that is different from the mock + // provider name. The caller may be trying to fool us via bug 33091107. EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(), provider + "!=" + loc.getProvider()); } - // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required - long identity = Binder.clearCallingIdentity(); - try { - mockProvider.setLocation(mock); - } finally { - Binder.restoreCallingIdentity(identity); - } - } + mockProvider.setLocation(mock); + }); } @Override - public void setTestProviderEnabled(String provider, boolean enabled, String opPackageName) { + public void setTestProviderEnabled(String provider, boolean enabled, String opPackageName) + throws RemoteException { if (!canCallerAccessMockLocation(opPackageName)) { return; } - synchronized (mLock) { + runFromBinderBlocking(() -> { MockProvider mockProvider = mMockProviders.get(provider); if (mockProvider == null) { throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); } - long identity = Binder.clearCallingIdentity(); - try { - mockProvider.setEnabled(enabled); - } finally { - Binder.restoreCallingIdentity(identity); - } - } + mockProvider.setEnabled(enabled); + }); } @Override public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime, - String opPackageName) { + String opPackageName) throws RemoteException { if (!canCallerAccessMockLocation(opPackageName)) { return; } - synchronized (mLock) { + runFromBinderBlocking(() -> { MockProvider mockProvider = mMockProviders.get(provider); if (mockProvider == null) { throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); } mockProvider.setStatus(status, extras, updateTime); - } - } - - private void log(String log) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Slog.d(TAG, log); - } + }); } @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; - synchronized (mLock) { + Runnable dump = () -> { if (args.length > 0 && args[0].equals("--gnssmetrics")) { if (mGnssMetricsProvider != null) { pw.append(mGnssMetricsProvider.getGnssMetricsAsProtoString()); @@ -3579,6 +3348,14 @@ public class LocationManagerService extends ILocationManager.Stub { if (mGnssBatchingInProgress) { pw.println(" GNSS batching in progress"); } + }; + + FutureTask<Void> task = new FutureTask<>(dump, null); + mHandler.postAtFrontOfQueue(task); + try { + task.get(); + } catch (InterruptedException | ExecutionException e) { + pw.println("error dumping: " + e); } } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 7f2a206e2a8d..4f21ee8d8624 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2429,16 +2429,21 @@ public class ActivityManagerService extends IActivityManager.Stub return mBackgroundLaunchBroadcasts; } + /** + * Returns true if the package {@code pkg1} running under user handle {@code uid1} is + * allowed association with the package {@code pkg2} running under user handle {@code uid2}. + * <p> If either of the packages are running as part of the core system, then the + * association is implicitly allowed. + */ boolean validateAssociationAllowedLocked(String pkg1, int uid1, String pkg2, int uid2) { if (mAllowedAssociations == null) { mAllowedAssociations = SystemConfig.getInstance().getAllowedAssociations(); } // Interactions with the system uid are always allowed, since that is the core system - // that everyone needs to be able to interact with. - if (UserHandle.getAppId(uid1) == SYSTEM_UID) { - return true; - } - if (UserHandle.getAppId(uid2) == SYSTEM_UID) { + // that everyone needs to be able to interact with. Also allow reflexive associations + // within the same uid. + if (uid1 == uid2 || UserHandle.getAppId(uid1) == SYSTEM_UID + || UserHandle.getAppId(uid2) == SYSTEM_UID) { return true; } // We won't allow this association if either pkg1 or pkg2 has a limit on the @@ -2454,6 +2459,8 @@ public class ActivityManagerService extends IActivityManager.Stub if (pkgs != null) { return pkgs.contains(pkg1); } + // If no explicit associations are provided in the manifest, then assume the app is + // allowed associations with any package. return true; } diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java index 9684f4c4ae19..2a000252d6f4 100644 --- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java +++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java @@ -126,21 +126,21 @@ public class NetworkMonitor extends StateMachine { private static final int DEFAULT_DATA_STALL_EVALUATION_TYPES = (1 << DATA_STALL_EVALUATION_TYPE_DNS); - static enum EvaluationResult { + enum EvaluationResult { VALIDATED(true), CAPTIVE_PORTAL(false); - final boolean isValidated; + final boolean mIsValidated; EvaluationResult(boolean isValidated) { - this.isValidated = isValidated; + this.mIsValidated = isValidated; } } - static enum ValidationStage { + enum ValidationStage { FIRST_VALIDATION(true), REVALIDATION(false); - final boolean isFirstValidation; + final boolean mIsFirstValidation; ValidationStage(boolean isFirstValidation) { - this.isFirstValidation = isFirstValidation; + this.mIsFirstValidation = isFirstValidation; } } @@ -251,7 +251,7 @@ public class NetworkMonitor extends StateMachine { // Start mReevaluateDelayMs at this value and double. private static final int INITIAL_REEVALUATE_DELAY_MS = 1000; - private static final int MAX_REEVALUATE_DELAY_MS = 10*60*1000; + private static final int MAX_REEVALUATE_DELAY_MS = 10 * 60 * 1000; // Before network has been evaluated this many times, ignore repeated reevaluate requests. private static final int IGNORE_REEVALUATE_ATTEMPTS = 5; private int mReevaluateToken = 0; @@ -261,7 +261,7 @@ public class NetworkMonitor extends StateMachine { // Stop blaming UID that requested re-evaluation after this many attempts. private static final int BLAME_FOR_EVALUATION_ATTEMPTS = 5; // Delay between reevaluations once a captive portal has been found. - private static final int CAPTIVE_PORTAL_REEVALUATE_DELAY_MS = 10*60*1000; + private static final int CAPTIVE_PORTAL_REEVALUATE_DELAY_MS = 10 * 60 * 1000; private static final int NUM_VALIDATION_LOG_LINES = 20; @@ -393,10 +393,17 @@ public class NetworkMonitor extends StateMachine { start(); } + /** + * Request the NetworkMonitor to reevaluate the network. + */ public void forceReevaluation(int responsibleUid) { sendMessage(CMD_FORCE_REEVALUATION, responsibleUid, 0); } + /** + * Send a notification to NetworkMonitor indicating that private DNS settings have changed. + * @param newCfg The new private DNS configuration. + */ public void notifyPrivateDnsSettingsChanged(PrivateDnsConfig newCfg) { // Cancel any outstanding resolutions. removeMessages(CMD_PRIVATE_DNS_SETTINGS_CHANGED); @@ -655,8 +662,9 @@ public class NetworkMonitor extends StateMachine { public boolean processMessage(Message message) { switch (message.what) { case CMD_REEVALUATE: - if (message.arg1 != mReevaluateToken || mUserDoesNotWant) + if (message.arg1 != mReevaluateToken || mUserDoesNotWant) { return HANDLED; + } // Don't bother validating networks that don't satisfy the default request. // This includes: // - VPNs which can be considered explicitly desired by the user and the @@ -813,9 +821,9 @@ public class NetworkMonitor extends StateMachine { } private boolean isStrictModeHostnameResolved() { - return (mPrivateDnsConfig != null) && - mPrivateDnsConfig.hostname.equals(mPrivateDnsProviderHostname) && - (mPrivateDnsConfig.ips.length > 0); + return (mPrivateDnsConfig != null) + && mPrivateDnsConfig.hostname.equals(mPrivateDnsProviderHostname) + && (mPrivateDnsConfig.ips.length > 0); } private void resolveStrictModeHostname() { @@ -852,9 +860,9 @@ public class NetworkMonitor extends StateMachine { private boolean sendPrivateDnsProbe() { // q.v. system/netd/server/dns/DnsTlsTransport.cpp - final String ONE_TIME_HOSTNAME_SUFFIX = "-dnsotls-ds.metric.gstatic.com"; - final String host = UUID.randomUUID().toString().substring(0, 8) + - ONE_TIME_HOSTNAME_SUFFIX; + final String oneTimeHostnameSuffix = "-dnsotls-ds.metric.gstatic.com"; + final String host = UUID.randomUUID().toString().substring(0, 8) + + oneTimeHostnameSuffix; final Stopwatch watch = new Stopwatch().start(); try { final InetAddress[] ips = mNetworkAgentInfo.network().getAllByName(host); @@ -966,7 +974,7 @@ public class NetworkMonitor extends StateMachine { // most one per address family. This ensures we only wait up to 20 seconds for TCP connections // to complete, regardless of how many IP addresses a host has. private static class OneAddressPerFamilyNetwork extends Network { - public OneAddressPerFamilyNetwork(Network network) { + OneAddressPerFamilyNetwork(Network network) { // Always bypass Private DNS. super(network.getPrivateDnsBypassingCopy()); } @@ -1000,7 +1008,8 @@ public class NetworkMonitor extends StateMachine { } public boolean getWifiScansAlwaysAvailableDisabled() { - return mDependencies.getSetting(mContext, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 0; + return mDependencies.getSetting( + mContext, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 0; } private String getCaptivePortalServerHttpsUrl() { @@ -1246,10 +1255,10 @@ public class NetworkMonitor extends StateMachine { // Time how long it takes to get a response to our request long responseTimestamp = SystemClock.elapsedRealtime(); - validationLog(probeType, url, "time=" + (responseTimestamp - requestTimestamp) + "ms" + - " ret=" + httpResponseCode + - " request=" + requestHeader + - " headers=" + urlConnection.getHeaderFields()); + validationLog(probeType, url, "time=" + (responseTimestamp - requestTimestamp) + "ms" + + " ret=" + httpResponseCode + + " request=" + requestHeader + + " headers=" + urlConnection.getHeaderFields()); // NOTE: We may want to consider an "HTTP/1.0 204" response to be a captive // portal. The only example of this seen so far was a captive portal. For // the time being go with prior behavior of assuming it's not a captive @@ -1267,7 +1276,7 @@ public class NetworkMonitor extends StateMachine { // sign-in to an empty page. Probably the result of a broken transparent proxy. // See http://b/9972012. validationLog(probeType, url, - "200 response with Content-length=0 interpreted as 204 response."); + "200 response with Content-length=0 interpreted as 204 response."); httpResponseCode = CaptivePortalProbeResult.SUCCESS_CODE; } else if (urlConnection.getContentLengthLong() == -1) { // When no Content-length (default value == -1), attempt to read a byte from the @@ -1309,7 +1318,7 @@ public class NetworkMonitor extends StateMachine { private final boolean mIsHttps; private volatile CaptivePortalProbeResult mResult = CaptivePortalProbeResult.FAILED; - public ProbeThread(boolean isHttps) { + ProbeThread(boolean isHttps) { mIsHttps = isHttps; } @@ -1443,8 +1452,10 @@ public class NetworkMonitor extends StateMachine { if (cellInfo.isRegistered()) { numRegisteredCellInfo++; if (numRegisteredCellInfo > 1) { - if (VDBG) logw("more than one registered CellInfo." + - " Can't tell which is active. Bailing."); + if (VDBG) { + logw("more than one registered CellInfo." + + " Can't tell which is active. Bailing."); + } return; } if (cellInfo instanceof CellInfoCdma) { @@ -1492,14 +1503,14 @@ public class NetworkMonitor extends StateMachine { } private int networkEventType(ValidationStage s, EvaluationResult r) { - if (s.isFirstValidation) { - if (r.isValidated) { + if (s.mIsFirstValidation) { + if (r.mIsValidated) { return NetworkEvent.NETWORK_FIRST_VALIDATION_SUCCESS; } else { return NetworkEvent.NETWORK_FIRST_VALIDATION_PORTAL_FOUND; } } else { - if (r.isValidated) { + if (r.mIsValidated) { return NetworkEvent.NETWORK_REVALIDATION_SUCCESS; } else { return NetworkEvent.NETWORK_REVALIDATION_PORTAL_FOUND; @@ -1517,7 +1528,7 @@ public class NetworkMonitor extends StateMachine { private void logValidationProbe(long durationMs, int probeType, int probeResult) { int[] transports = mNetworkAgentInfo.networkCapabilities.getTransportTypes(); - boolean isFirstValidation = validationStage().isFirstValidation; + boolean isFirstValidation = validationStage().mIsFirstValidation; ValidationProbeEvent ev = new ValidationProbeEvent(); ev.probeType = ValidationProbeEvent.makeProbeType(probeType, isFirstValidation); ev.returnCode = probeResult; @@ -1535,10 +1546,20 @@ public class NetworkMonitor extends StateMachine { return new Random(); } + /** + * Get the value of a global integer setting. + * @param symbol Name of the setting + * @param defaultValue Value to return if the setting is not defined. + */ public int getSetting(Context context, String symbol, int defaultValue) { return Settings.Global.getInt(context.getContentResolver(), symbol, defaultValue); } + /** + * Get the value of a global String setting. + * @param symbol Name of the setting + * @param defaultValue Value to return if the setting is not defined. + */ public String getSetting(Context context, String symbol, String defaultValue) { final String value = Settings.Global.getString(context.getContentResolver(), symbol); return value != null ? value : defaultValue; diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java index d56b167b9a75..7daf71dda73b 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java @@ -34,32 +34,53 @@ import java.util.ArrayList; * @hide */ public class TetheringDependencies { + /** + * Get a reference to the offload hardware interface to be used by tethering. + */ public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) { return new OffloadHardwareInterface(h, log); } + /** + * Get a reference to the UpstreamNetworkMonitor to be used by tethering. + */ public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target, SharedLog log, int what) { return new UpstreamNetworkMonitor(ctx, target, log, what); } + /** + * Get a reference to the IPv6TetheringCoordinator to be used by tethering. + */ public IPv6TetheringCoordinator getIPv6TetheringCoordinator( ArrayList<IpServer> notifyList, SharedLog log) { return new IPv6TetheringCoordinator(notifyList, log); } + /** + * Get dependencies to be used by IpServer. + */ public IpServer.Dependencies getIpServerDependencies() { return new IpServer.Dependencies(); } + /** + * Indicates whether tethering is supported on the device. + */ public boolean isTetheringSupported() { return true; } + /** + * Get the NetworkRequest that should be fulfilled by the default network. + */ public NetworkRequest getDefaultNetworkRequest() { return null; } + /** + * Get a reference to the EntitlementManager to be used by tethering. + */ public EntitlementManager getEntitlementManager(Context ctx, SharedLog log, MockableSystemProperties systemProperties) { return new EntitlementManager(ctx, log, systemProperties); diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java index 1f0f94a7bf0b..5210270051c0 100644 --- a/services/core/java/com/android/server/hdmi/Constants.java +++ b/services/core/java/com/android/server/hdmi/Constants.java @@ -300,7 +300,7 @@ final class Constants { * <p>Default is true. */ static final String PROPERTY_ARC_SUPPORT = - "persist.sys.hdmi.property_arc_support"; + "persist.sys.hdmi.property_arc_support"; /** * Property to save the audio port to switch to when system audio control is on. @@ -310,7 +310,7 @@ final class Constants { * <p>Default is ARC port. */ static final String PROPERTY_SYSTEM_AUDIO_MODE_AUDIO_PORT = - "persist.sys.hdmi.property_sytem_audio_mode_audio_port"; + "persist.sys.hdmi.property_sytem_audio_mode_audio_port"; /** * Property to save the ARC port id on system audio device. @@ -320,6 +320,15 @@ final class Constants { "persist.sys.hdmi.property_sytem_audio_device_arc_port"; /** + * Property to indicate if a CEC audio device should forward volume keys when system audio mode + * is off. + * + * <p>Default is false. + */ + static final String PROPERTY_CEC_AUDIO_DEVICE_FORWARD_VOLUME_KEYS_SYSTEM_AUDIO_MODE_OFF = + "persist.sys.hdmi.property_cec_audio_device_forward_volume_keys_system_audio_mode_off"; + + /** * Property to strip local audio of amplifier and use local speaker * when TV does not support system audio mode. * diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java index 9690ba877c1e..2ac04d1e2de0 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java @@ -25,6 +25,7 @@ import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.media.AudioSystem; import android.os.SystemProperties; +import android.provider.Settings.Global; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -50,6 +51,11 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice { private boolean mTvSystemAudioModeSupport; + // Whether the auido system will turn TV off when it's powering off + private boolean mAutoTvOff; + // Whether the auido system will broadcast standby to the system when it's powering off + private boolean mAutoDeviceOff; + // Whether ARC is available or not. "true" means that ARC is established between TV and // AVR as audio receiver. @ServiceThreadOnly private boolean mArcEstablished = false; @@ -60,6 +66,10 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice { // TODO(amyjojo) make System Audio Control controllable by users /*mSystemAudioControlFeatureEnabled = mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);*/ + mAutoDeviceOff = mService.readBooleanSetting( + Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, true); + mAutoTvOff = mService.readBooleanSetting( + Global.HDMI_CONTROL_AUTO_TV_OFF_ENABLED, true); } @Override @@ -74,6 +84,21 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice { mSystemAudioActivated ? "true" : "false"); } terminateSystemAudioMode(); + + HdmiLogger.debug(TAG + " onStandby, initiatedByCec:" + initiatedByCec + + ", mAutoDeviceOff: " + mAutoDeviceOff + ", mAutoTvOff: " + mAutoTvOff); + if (!mService.isControlEnabled() || initiatedByCec) { + return; + } + if (mAutoDeviceOff) { + mService.sendCecCommand( + HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_BROADCAST)); + } else if (mAutoTvOff) { + mService.sendCecCommand( + HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV)); + } + return; + } @Override @@ -491,4 +516,17 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice { return mArcEstablished; } } + + @ServiceThreadOnly + protected void setAutoTvOff(boolean autoTvOff) { + assertRunOnServiceThread(); + mAutoTvOff = autoTvOff; + } + + @Override + @ServiceThreadOnly + void setAutoDeviceOff(boolean autoDeviceOff) { + assertRunOnServiceThread(); + mAutoDeviceOff = autoDeviceOff; + } } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 3420b263eac5..e3a4084ab7f3 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -592,6 +592,11 @@ public class HdmiControlService extends SystemService { } // No need to propagate to HAL. break; + case Global.HDMI_CONTROL_AUTO_TV_OFF_ENABLED: + if (isAudioSystemDevice()) { + audioSystem().setAutoTvOff(enabled); + } + break; case Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED: if (isTvDeviceEnabled()) { tv().setSystemAudioControlFeatureEnabled(enabled); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 5a7739c49b42..d45869e07586 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -291,6 +291,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub new DebugFlag("debug.optimize_startinput", false); } + @UserIdInt + private int mLastSwitchUserId; final Context mContext; final Resources mRes; @@ -1436,6 +1438,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e); } + mLastSwitchUserId = userId; + // mSettings should be created before buildInputMethodListLocked mSettings = new InputMethodSettings( mRes, context.getContentResolver(), mMethodMap, mMethodList, userId, !mSystemReady); @@ -1523,6 +1527,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (DEBUG) Slog.d(TAG, "Switching user stage 3/3. newUserId=" + newUserId + " selectedIme=" + mSettings.getSelectedInputMethod()); + + mLastSwitchUserId = newUserId; } void updateCurrentProfileIds() { @@ -4424,6 +4430,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return refreshDebugProperties(); } + if ("get-last-switch-user-id".equals(cmd)) { + return mService.getLastSwitchUserId(this); + } + // For existing "adb shell ime <command>". if ("ime".equals(cmd)) { final String imeCommand = getNextArg(); @@ -4516,6 +4526,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // ---------------------------------------------------------------------- // Shell command handlers: + @BinderThread + @ShellCommandResult + private int getLastSwitchUserId(@NonNull ShellCommand shellCommand) { + synchronized (mMethodMap) { + shellCommand.getOutPrintWriter().println(mLastSwitchUserId); + return ShellCommandResult.SUCCESS; + } + } + /** * Handles {@code adb shell ime list}. * @param shellCommand {@link ShellCommand} object that is handling this command. diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 139d8ac6a8d5..b70c64edc2cc 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -34,7 +34,6 @@ import android.media.session.ISessionControllerCallback; import android.media.session.MediaController; import android.media.session.MediaController.PlaybackInfo; import android.media.session.MediaSession; -import android.media.session.ParcelableVolumeInfo; import android.media.session.PlaybackState; import android.net.Uri; import android.os.Binder; @@ -628,7 +627,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { if (mDestroyed) { return; } - ParcelableVolumeInfo info = mController.getVolumeAttributes(); + PlaybackInfo info = mController.getVolumeAttributes(); for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) { ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i); try { @@ -1233,14 +1232,14 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override - public ParcelableVolumeInfo getVolumeAttributes() { + public PlaybackInfo getVolumeAttributes() { int volumeType; AudioAttributes attributes; synchronized (mLock) { if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_REMOTE) { int current = mOptimisticVolume != -1 ? mOptimisticVolume : mCurrentVolume; - return new ParcelableVolumeInfo( - mVolumeType, mAudioAttrs, mVolumeControlType, mMaxVolume, current); + return new PlaybackInfo(mVolumeType, mVolumeControlType, mMaxVolume, current, + mAudioAttrs); } volumeType = mVolumeType; attributes = mAudioAttrs; @@ -1248,8 +1247,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { int stream = AudioAttributes.toLegacyStreamType(attributes); int max = mAudioManager.getStreamMaxVolume(stream); int current = mAudioManager.getStreamVolume(stream); - return new ParcelableVolumeInfo( - volumeType, attributes, VolumeProvider.VOLUME_CONTROL_ABSOLUTE, max, current); + return new PlaybackInfo(volumeType, VolumeProvider.VOLUME_CONTROL_ABSOLUTE, max, + current, attributes); } @Override diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index 88d73fb10902..c222e6948a3d 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -767,6 +767,23 @@ abstract public class ManagedServices { return installed; } + protected Set<String> getAllowedPackages() { + final Set<String> allowedPackages = new ArraySet<>(); + for (int k = 0; k < mApproved.size(); k++) { + ArrayMap<Boolean, ArraySet<String>> allowedByType = mApproved.valueAt(k); + for (int i = 0; i < allowedByType.size(); i++) { + final ArraySet<String> allowed = allowedByType.valueAt(i); + for (int j = 0; j < allowed.size(); j++) { + String pkgName = getPackageName(allowed.valueAt(j)); + if (!TextUtils.isEmpty(pkgName)) { + allowedPackages.add(pkgName); + } + } + } + } + return allowedPackages; + } + private void trimApprovedListsAccordingToInstalledServices() { int N = mApproved.size(); for (int i = 0 ; i < N; i++) { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 655daf89dda4..c68e0f98325e 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1701,8 +1701,16 @@ public class NotificationManagerService extends SystemService { } private void sendRegisteredOnlyBroadcast(String action) { - getContext().sendBroadcastAsUser(new Intent(action) - .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null); + Intent intent = new Intent(action); + getContext().sendBroadcastAsUser(intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), + UserHandle.ALL, null); + // explicitly send the broadcast to all DND packages, even if they aren't currently running + intent.setFlags(0); + final Set<String> dndApprovedPackages = mConditionProviders.getAllowedPackages(); + for (String pkg : dndApprovedPackages) { + intent.setPackage(pkg); + getContext().sendBroadcastAsUser(intent, UserHandle.ALL); + } } @Override @@ -2641,6 +2649,9 @@ public class NotificationManagerService extends SystemService { // Zen mConditionProviders.onPackagesChanged(true, packages, uids); + // Snoozing + mSnoozeHelper.clearData(UserHandle.getUserId(uid), packageName); + // Reset notification preferences if (!fromApp) { mPreferencesHelper.onPackagesChanged( diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java index 2b581d601ad5..abc98412e126 100644 --- a/services/core/java/com/android/server/notification/SnoozeHelper.java +++ b/services/core/java/com/android/server/notification/SnoozeHelper.java @@ -17,6 +17,7 @@ package com.android.server.notification; import android.annotation.NonNull; import android.app.AlarmManager; +import android.app.Notification; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; @@ -163,7 +164,6 @@ public class SnoozeHelper { mSnoozedNotifications.get(userId).get(pkg); if (recordsForPkg != null) { final Set<Map.Entry<String, NotificationRecord>> records = recordsForPkg.entrySet(); - String key = null; for (Map.Entry<String, NotificationRecord> record : records) { final StatusBarNotification sbn = record.getValue().sbn; if (Objects.equals(sbn.getTag(), tag) && sbn.getId() == id) { @@ -305,6 +305,30 @@ public class SnoozeHelper { } } + protected void clearData(int userId, String pkg) { + ArrayMap<String, ArrayMap<String, NotificationRecord>> records = + mSnoozedNotifications.get(userId); + if (records == null) { + return; + } + ArrayMap<String, NotificationRecord> pkgRecords = records.get(pkg); + if (pkgRecords == null) { + return; + } + for (int i = pkgRecords.size() - 1; i >= 0; i--) { + final NotificationRecord r = pkgRecords.removeAt(i); + if (r != null) { + mPackages.remove(r.getKey()); + mUsers.remove(r.getKey()); + final PendingIntent pi = createPendingIntent(pkg, r.getKey(), userId); + mAm.cancel(pi); + MetricsLogger.action(r.getLogMaker() + .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED) + .setType(MetricsProto.MetricsEvent.TYPE_DISMISS)); + } + } + } + private PendingIntent createPendingIntent(String pkg, String key, int userId) { return PendingIntent.getBroadcast(mContext, REQUEST_CODE_REPOST, diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index e5b1878650d3..afc0b7230e27 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -375,8 +375,7 @@ public class ZenModeHelper { newConfig = mConfig.copy(); for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) { ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i)); - if (rule.component.getPackageName().equals(packageName) - && canManageAutomaticZenRule(rule)) { + if (rule.pkg.equals(packageName) && canManageAutomaticZenRule(rule)) { newConfig.automaticRules.removeAt(i); } } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 982daa5df2d5..3cb771449e09 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -1374,7 +1374,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { "Missing existing base package"); } // Default to require only if existing base has fs-verity. - mVerityFound = params.mode == SessionParams.MODE_INHERIT_EXISTING + mVerityFound = PackageManagerServiceUtils.isApkVerityEnabled() + && params.mode == SessionParams.MODE_INHERIT_EXISTING && VerityUtils.hasFsverity(pkgInfo.applicationInfo.getBaseCodePath()); try { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index fe89be6f73c4..522ab0bf93e2 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -8572,7 +8572,7 @@ public class PackageManagerService extends IPackageManager.Stub * match one in a trusted source, and should be done separately. */ private boolean canSkipForcedApkVerification(String apkPath) { - if (!PackageManagerServiceUtils.isLegacyApkVerityMode()) { + if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) { return VerityUtils.hasFsverity(apkPath); } @@ -16866,10 +16866,11 @@ public class PackageManagerService extends IPackageManager.Stub */ private void setUpFsVerityIfPossible(PackageParser.Package pkg) throws InstallerException, PrepareFailure, IOException, DigestException, NoSuchAlgorithmException { - if (!PackageManagerServiceUtils.isApkVerityEnabled()) { + final boolean standardMode = PackageManagerServiceUtils.isApkVerityEnabled(); + final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityEnabled(); + if (!standardMode && !legacyMode) { return; } - final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityMode(); // Collect files we care for fs-verity setup. ArrayMap<String, String> fsverityCandidates = new ArrayMap<>(); diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index 25169a24d932..6134d3098e3b 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -555,19 +555,19 @@ public class PackageManagerServiceUtils { /** Standard fs-verity. */ private static final int FSVERITY_ENABLED = 2; - /** Returns true if APK Verity is enabled. */ + /** Returns true if standard APK Verity is enabled. */ static boolean isApkVerityEnabled() { - int mode = SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED); - return mode == FSVERITY_LEGACY || mode == FSVERITY_ENABLED; + return SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED) == FSVERITY_ENABLED; } - static boolean isLegacyApkVerityMode() { + static boolean isLegacyApkVerityEnabled() { return SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED) == FSVERITY_LEGACY; } /** Returns true to force apk verification if the updated package (in /data) is a priv app. */ static boolean isApkVerificationForced(@Nullable PackageSetting disabledPs) { - return disabledPs != null && disabledPs.isPrivileged() && isApkVerityEnabled(); + return disabledPs != null && disabledPs.isPrivileged() && ( + isApkVerityEnabled() || isLegacyApkVerityEnabled()); } /** diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index 5adc248cee79..c3f20aab568e 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -755,9 +755,10 @@ public class Notifier { }; /** - * Plays the wireless charging sound for both wireless and non-wireless charging + * If enabled, plays a sound and/or vibration when wireless or non-wireless charging has started */ - private void playChargingStartedSound(@UserIdInt int userId) { + private void playChargingStartedFeedback(@UserIdInt int userId) { + playChargingStartedVibration(userId); final String soundPath = Settings.Global.getString(mContext.getContentResolver(), Settings.Global.CHARGING_STARTED_SOUND); if (isChargingFeedbackEnabled(userId) && soundPath != null) { @@ -773,8 +774,7 @@ public class Notifier { } private void showWirelessChargingStarted(int batteryLevel, @UserIdInt int userId) { - playWirelessChargingVibration(userId); - playChargingStartedSound(userId); + playChargingStartedFeedback(userId); if (mStatusBarManagerInternal != null) { mStatusBarManagerInternal.showChargingAnimation(batteryLevel); } @@ -782,7 +782,7 @@ public class Notifier { } private void showWiredChargingStarted(@UserIdInt int userId) { - playChargingStartedSound(userId); + playChargingStartedFeedback(userId); mSuspendBlocker.release(); } @@ -790,7 +790,7 @@ public class Notifier { mTrustManager.setDeviceLockedForUser(userId, true /*locked*/); } - private void playWirelessChargingVibration(@UserIdInt int userId) { + private void playChargingStartedVibration(@UserIdInt int userId) { final boolean vibrateEnabled = Settings.Secure.getIntForUser(mContext.getContentResolver(), Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0; if (vibrateEnabled && isChargingFeedbackEnabled(userId)) { diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java index f262f6dabbe0..a60f16d7ca27 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java @@ -15,8 +15,14 @@ */ package com.android.server.power.batterysaver; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; import android.database.ContentObserver; import android.os.Handler; import android.os.PowerManager; @@ -26,6 +32,7 @@ import android.provider.Settings.Global; import android.util.Slog; import android.util.proto.ProtoOutputStream; +import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; @@ -46,6 +53,8 @@ import java.io.PrintWriter; */ public class BatterySaverStateMachine { private static final String TAG = "BatterySaverStateMachine"; + private static final String DYNAMIC_MODE_NOTIF_CHANNEL_ID = "dynamic_mode_notification"; + private static final int DYNAMIC_MODE_NOTIFICATION_ID = 1992; private final Object mLock; private static final boolean DEBUG = BatterySaverPolicy.DEBUG; @@ -484,6 +493,13 @@ public class BatterySaverStateMachine { } mBatterySaverController.enableBatterySaver(enable, intReason); + // Handle triggering the notification to show/hide when appropriate + if (intReason == BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON) { + runOnBgThread(this::triggerDynamicModeNotification); + } else if (!enable) { + runOnBgThread(this::hideDynamicModeNotification); + } + if (DEBUG) { Slog.d(TAG, "Battery saver: Enabled=" + enable + " manual=" + manual @@ -491,6 +507,44 @@ public class BatterySaverStateMachine { } } + private void triggerDynamicModeNotification() { + NotificationManager manager = mContext.getSystemService(NotificationManager.class); + ensureNotificationChannelExists(manager); + + manager.notify(DYNAMIC_MODE_NOTIFICATION_ID, buildNotification()); + } + + private void ensureNotificationChannelExists(NotificationManager manager) { + NotificationChannel channel = new NotificationChannel( + DYNAMIC_MODE_NOTIF_CHANNEL_ID, + mContext.getText( + R.string.dynamic_mode_notification_channel_name), + NotificationManager.IMPORTANCE_DEFAULT); + channel.setSound(null, null); + manager.createNotificationChannel(channel); + } + + private Notification buildNotification() { + Resources res = mContext.getResources(); + Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + PendingIntent batterySaverIntent = PendingIntent.getActivity( + mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT); + + return new Notification.Builder(mContext, DYNAMIC_MODE_NOTIF_CHANNEL_ID) + .setSmallIcon(R.drawable.ic_battery) + .setContentTitle(res.getString(R.string.dynamic_mode_notification_title)) + .setContentText(res.getString(R.string.dynamic_mode_notification_summary)) + .setContentIntent(batterySaverIntent) + .setOnlyAlertOnce(true) + .build(); + } + + private void hideDynamicModeNotification() { + NotificationManager manager = mContext.getSystemService(NotificationManager.class); + manager.cancel(DYNAMIC_MODE_NOTIFICATION_ID); + } + @GuardedBy("mLock") private void updateSnoozingLocked(boolean snoozing, String reason) { if (mBatterySaverSnoozing == snoozing) { diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index f00877030145..01bff07d1bb0 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -350,12 +350,34 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } private void notifyWallpaperColorsChanged(@NonNull WallpaperData wallpaper, int which) { + if (wallpaper.connection != null) { + wallpaper.connection.forEachDisplayConnector(connector -> { + notifyWallpaperColorsChangedOnDisplay(wallpaper, which, connector.mDisplayId); + }); + } else { // Lock wallpaper does not have WallpaperConnection. + notifyWallpaperColorsChangedOnDisplay(wallpaper, which, DEFAULT_DISPLAY); + } + } + + private RemoteCallbackList<IWallpaperManagerCallback> getWallpaperCallbacks(int userId, + int displayId) { + RemoteCallbackList<IWallpaperManagerCallback> listeners = null; + final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> displayListeners = + mColorsChangedListeners.get(userId); + if (displayListeners != null) { + listeners = displayListeners.get(displayId); + } + return listeners; + } + + private void notifyWallpaperColorsChangedOnDisplay(@NonNull WallpaperData wallpaper, int which, + int displayId) { boolean needsExtraction; synchronized (mLock) { final RemoteCallbackList<IWallpaperManagerCallback> currentUserColorListeners = - mColorsChangedListeners.get(wallpaper.userId); + getWallpaperCallbacks(wallpaper.userId, displayId); final RemoteCallbackList<IWallpaperManagerCallback> userAllColorListeners = - mColorsChangedListeners.get(UserHandle.USER_ALL); + getWallpaperCallbacks(UserHandle.USER_ALL, displayId); // No-op until someone is listening to it. if (emptyCallbackList(currentUserColorListeners) && emptyCallbackList(userAllColorListeners)) { @@ -363,7 +385,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } if (DEBUG) { - Slog.v(TAG, "notifyWallpaperColorsChanged " + which); + Slog.v(TAG, "notifyWallpaperColorsChangedOnDisplay " + which); } needsExtraction = wallpaper.primaryColors == null; @@ -371,7 +393,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // Let's notify the current values, it's fine if it's null, it just means // that we don't know yet. - notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId); + notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId, displayId); if (needsExtraction) { extractColors(wallpaper); @@ -381,7 +403,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub return; } } - notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId); + notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId, displayId); } } @@ -390,14 +412,14 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } private void notifyColorListeners(@NonNull WallpaperColors wallpaperColors, int which, - int userId) { + int userId, int displayId) { final IWallpaperManagerCallback keyguardListener; final ArrayList<IWallpaperManagerCallback> colorListeners = new ArrayList<>(); synchronized (mLock) { final RemoteCallbackList<IWallpaperManagerCallback> currentUserColorListeners = - mColorsChangedListeners.get(userId); + getWallpaperCallbacks(userId, displayId); final RemoteCallbackList<IWallpaperManagerCallback> userAllColorListeners = - mColorsChangedListeners.get(UserHandle.USER_ALL); + getWallpaperCallbacks(UserHandle.USER_ALL, displayId); keyguardListener = mKeyguardListener; if (currentUserColorListeners != null) { @@ -427,7 +449,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } - if (keyguardListener != null) { + // Only shows Keyguard on default display + if (keyguardListener != null && displayId == DEFAULT_DISPLAY) { try { keyguardListener.onWallpaperColorsChanged(wallpaperColors, which, userId); } catch (RemoteException e) { @@ -446,6 +469,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub String cropFile = null; int wallpaperId; + if (wallpaper.equals(mFallbackWallpaper)) { + extractDefaultImageWallpaperColors(); + return; + } + synchronized (mLock) { // Not having a wallpaperComponent means it's a lock screen wallpaper. final boolean imageWallpaper = mImageWallpaper.equals(wallpaper.wallpaperComponent) @@ -482,6 +510,39 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } + private void extractDefaultImageWallpaperColors() { + synchronized (mLock) { + if (mFallbackWallpaper.primaryColors != null) return; + } + + if (DEBUG) Slog.d(TAG, "Extract default image wallpaper colors"); + WallpaperColors colors = null; + final InputStream is = WallpaperManager.openDefaultWallpaper(mContext, FLAG_SYSTEM); + if (is != null) { + try { + final BitmapFactory.Options options = new BitmapFactory.Options(); + final Bitmap bitmap = BitmapFactory.decodeStream(is, null, options); + if (bitmap != null) { + colors = WallpaperColors.fromBitmap(bitmap); + bitmap.recycle(); + } + } catch (OutOfMemoryError e) { + Slog.w(TAG, "Can't decode default wallpaper stream", e); + } finally { + IoUtils.closeQuietly(is); + } + } + + if (colors == null) { + Slog.e(TAG, "Extract default image wallpaper colors failed"); + return; + } + + synchronized (mLock) { + mFallbackWallpaper.primaryColors = colors; + } + } + /** * Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped * for display. @@ -696,6 +757,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub targetWallpaper.connection.removeDisplayConnector(displayId); removeDisplayData(displayId); } + for (int i = mColorsChangedListeners.size() - 1; i >= 0; i--) { + final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> callbacks = + mColorsChangedListeners.valueAt(i); + callbacks.delete(displayId); + } } } @@ -706,9 +772,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub /** * Map of color listeners per user id. - * The key will be the id of a user or UserHandle.USER_ALL - for wildcard listeners. + * The first key will be the id of a user or UserHandle.USER_ALL - for wildcard listeners. + * The secondary key will be the display id, which means which display the listener is + * interested in. */ - private final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> + private final SparseArray<SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>> mColorsChangedListeners; private WallpaperData mLastWallpaper; private IWallpaperManagerCallback mKeyguardListener; @@ -1228,9 +1296,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub /** * Called by a live wallpaper if its colors have changed. * @param primaryColors representation of wallpaper primary colors + * @param displayId for which display */ @Override - public void onWallpaperColorsChanged(WallpaperColors primaryColors) { + public void onWallpaperColorsChanged(WallpaperColors primaryColors, int displayId) { int which; synchronized (mLock) { // Do not broadcast changes on ImageWallpaper since it's handled @@ -1243,14 +1312,16 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // Live wallpapers always are system wallpapers. which = FLAG_SYSTEM; - // It's also the lock screen wallpaper when we don't have a bitmap in there - WallpaperData lockedWallpaper = mLockWallpaperMap.get(mWallpaper.userId); - if (lockedWallpaper == null) { - which |= FLAG_LOCK; + // It's also the lock screen wallpaper when we don't have a bitmap in there. + if (displayId == DEFAULT_DISPLAY) { + final WallpaperData lockedWallpaper = mLockWallpaperMap.get(mWallpaper.userId); + if (lockedWallpaper == null) { + which |= FLAG_LOCK; + } } } if (which != 0) { - notifyWallpaperColorsChanged(mWallpaper, which); + notifyWallpaperColorsChangedOnDisplay(mWallpaper, which, displayId); } } @@ -1277,16 +1348,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub Slog.w(TAG, "Failed to set ambient mode state", e); } } - // TODO(multi-display) So far, we have shared the same wallpaper on each display. - // Once we have multiple wallpapers on multiple displays, please complete here. - if (displayId == DEFAULT_DISPLAY) { - try { - // This will trigger onComputeColors in the wallpaper engine. - // It's fine to be locked in here since the binder is oneway. - connector.mEngine.requestWallpaperColors(); - } catch (RemoteException e) { - Slog.w(TAG, "Failed to request wallpaper colors", e); - } + try { + // This will trigger onComputeColors in the wallpaper engine. + // It's fine to be locked in here since the binder is oneway. + connector.mEngine.requestWallpaperColors(); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to request wallpaper colors", e); } } } @@ -1681,6 +1748,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub FgThread.getHandler().post(() -> { notifyWallpaperColorsChanged(systemWallpaper, FLAG_SYSTEM); notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK); + notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM); }); } @@ -1743,6 +1811,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // When clearing a wallpaper, broadcast new valid colors if (data != null) { notifyWallpaperColorsChanged(data, which); + notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM); } } @@ -2084,35 +2153,47 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } @Override - public void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId) { + public void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId, + int displayId) { userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "registerWallpaperColorsCallback", null); synchronized (mLock) { - RemoteCallbackList<IWallpaperManagerCallback> userColorsChangedListeners = - mColorsChangedListeners.get(userId); - if (userColorsChangedListeners == null) { - userColorsChangedListeners = new RemoteCallbackList<>(); - mColorsChangedListeners.put(userId, userColorsChangedListeners); + SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> + userDisplayColorsChangedListeners = mColorsChangedListeners.get(userId); + if (userDisplayColorsChangedListeners == null) { + userDisplayColorsChangedListeners = new SparseArray<>(); + mColorsChangedListeners.put(userId, userDisplayColorsChangedListeners); + } + RemoteCallbackList<IWallpaperManagerCallback> displayChangedListeners = + userDisplayColorsChangedListeners.get(displayId); + if (displayChangedListeners == null) { + displayChangedListeners = new RemoteCallbackList<>(); + userDisplayColorsChangedListeners.put(displayId, displayChangedListeners); } - userColorsChangedListeners.register(cb); + displayChangedListeners.register(cb); } } @Override - public void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId) { + public void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId, + int displayId) { userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "unregisterWallpaperColorsCallback", null); synchronized (mLock) { - final RemoteCallbackList<IWallpaperManagerCallback> userColorsChangedListeners = - mColorsChangedListeners.get(userId); - if (userColorsChangedListeners != null) { - userColorsChangedListeners.unregister(cb); + SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> + userDisplayColorsChangedListeners = mColorsChangedListeners.get(userId); + if (userDisplayColorsChangedListeners != null) { + RemoteCallbackList<IWallpaperManagerCallback> displayChangedListeners = + userDisplayColorsChangedListeners.get(displayId); + if (displayChangedListeners != null) { + displayChangedListeners.unregister(cb); + } } } } /** - * TODO(b/115486823) Extends this method with specific display. + * TODO(multi-display) Extends this method with specific display. * Propagate ambient state to wallpaper engine. * * @param inAmbientMode {@code true} when in ambient mode, {@code false} otherwise. @@ -2125,7 +2206,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub final WallpaperData data = mWallpaperMap.get(mCurrentUserId); if (data != null && data.connection != null && data.connection.mInfo != null && data.connection.mInfo.supportsAmbientMode()) { - // TODO(b/115486823) Extends this method with specific display. + // TODO(multi-display) Extends this method with specific display. engine = data.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine; } else { engine = null; @@ -2151,7 +2232,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } @Override - public WallpaperColors getWallpaperColors(int which, int userId) throws RemoteException { + public WallpaperColors getWallpaperColors(int which, int userId, int displayId) + throws RemoteException { if (which != FLAG_LOCK && which != FLAG_SYSTEM) { throw new IllegalArgumentException("which should be either FLAG_LOCK or FLAG_SYSTEM"); } @@ -2169,7 +2251,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // Try to get the system wallpaper anyway since it might // also be the lock screen wallpaper if (wallpaperData == null) { - wallpaperData = mWallpaperMap.get(userId); + wallpaperData = findWallpaperAtDisplay(userId, displayId); } if (wallpaperData == null) { @@ -2187,6 +2269,15 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } + private WallpaperData findWallpaperAtDisplay(int userId, int displayId) { + if (mFallbackWallpaper != null && mFallbackWallpaper.connection != null + && mFallbackWallpaper.connection.containsDisplay(displayId)) { + return mFallbackWallpaper; + } else { + return mWallpaperMap.get(userId); + } + } + @Override public ParcelFileDescriptor setWallpaper(String name, String callingPackage, Rect cropHint, boolean allowBackup, Bundle extras, int which, @@ -2382,6 +2473,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub if (shouldNotifyColors) { notifyWallpaperColorsChanged(wallpaper, which); + notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM); } } diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index fe0b5c250da8..caebf1528270 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -1066,93 +1066,19 @@ final class AccessibilityController { final int visibleWindowCount = visibleWindows.size(); HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>(); - for (int i = visibleWindowCount - 1; i >= 0; i--) { - final WindowState windowState = visibleWindows.valueAt(i); - final int flags = windowState.mAttrs.flags; - final Task task = windowState.getTask(); - // If the window is part of a task that we're finished with - ignore. - if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) { - continue; - } - - // Ignore non-touchable windows, except the split-screen divider, which is - // occasionally non-touchable but still useful for identifying split-screen - // mode. - if (((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) - && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)) { - continue; - } + // Iterate until we figure out what is touchable for the entire screen. + for (int i = visibleWindowCount - 1; i >= 0 && !unaccountedSpace.isEmpty(); i--) { + final WindowState windowState = visibleWindows.valueAt(i); - // Compute the bounds in the screen. final Rect boundsInScreen = mTempRect; computeWindowBoundsInScreen(windowState, boundsInScreen); - // If the window is completely covered by other windows - ignore. - if (unaccountedSpace.quickReject(boundsInScreen)) { - continue; - } - - // Add windows of certain types not covered by modal windows. - if (isReportedWindowType(windowState.mAttrs.type)) { - // Add the window to the ones to be reported. + if (windowMattersToAccessibility(windowState, boundsInScreen, unaccountedSpace, + skipRemainingWindowsForTasks)) { addPopulatedWindowInfo(windowState, boundsInScreen, windows, addedWindows); - if (windowState.isFocused()) { - focusedWindowAdded = true; - } - } - - if (windowState.mAttrs.type != - WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) { - - // Account for the space this window takes if the window - // is not an accessibility overlay which does not change - // the reported windows. - unaccountedSpace.op(boundsInScreen, unaccountedSpace, - Region.Op.REVERSE_DIFFERENCE); - - // If a window is modal it prevents other windows from being touched - if ((flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) { - // Account for all space in the task, whether the windows in it are - // touchable or not. The modal window blocks all touches from the task's - // area. - unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace, - Region.Op.REVERSE_DIFFERENCE); - - if (task != null) { - // If the window is associated with a particular task, we can skip the - // rest of the windows for that task. - skipRemainingWindowsForTasks.add(task.mTaskId); - continue; - } else { - // If the window is not associated with a particular task, then it is - // globally modal. In this case we can skip all remaining windows. - break; - } - } - } - - // We figured out what is touchable for the entire screen - done. - if (unaccountedSpace.isEmpty()) { - break; - } - } - - // Always report the focused window. - if (!focusedWindowAdded) { - for (int i = visibleWindowCount - 1; i >= 0; i--) { - WindowState windowState = visibleWindows.valueAt(i); - if (windowState.isFocused()) { - // Compute the bounds in the screen. - Rect boundsInScreen = mTempRect; - computeWindowBoundsInScreen(windowState, boundsInScreen); - - // Add the window to the ones to be reported. - addPopulatedWindowInfo( - windowState, boundsInScreen, windows, addedWindows); - break; - } + updateUnaccountedSpace(windowState, boundsInScreen, unaccountedSpace, + skipRemainingWindowsForTasks); } } @@ -1221,6 +1147,73 @@ final class AccessibilityController { clearAndRecycleWindows(windows); } + private boolean windowMattersToAccessibility(WindowState windowState, Rect boundsInScreen, + Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks) { + if (windowState.isFocused()) { + return true; + } + + // If the window is part of a task that we're finished with - ignore. + final Task task = windowState.getTask(); + if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) { + return false; + } + + // Ignore non-touchable windows, except the split-screen divider, which is + // occasionally non-touchable but still useful for identifying split-screen + // mode. + if (((windowState.mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) + && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)) { + return false; + } + + // If the window is completely covered by other windows - ignore. + if (unaccountedSpace.quickReject(boundsInScreen)) { + return false; + } + + // Add windows of certain types not covered by modal windows. + if (isReportedWindowType(windowState.mAttrs.type)) { + return true; + } + + return false; + } + + private void updateUnaccountedSpace(WindowState windowState, Rect boundsInScreen, + Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks) { + if (windowState.mAttrs.type + != WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) { + + // Account for the space this window takes if the window + // is not an accessibility overlay which does not change + // the reported windows. + unaccountedSpace.op(boundsInScreen, unaccountedSpace, + Region.Op.REVERSE_DIFFERENCE); + + // If a window is modal it prevents other windows from being touched + if ((windowState.mAttrs.flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) { + // Account for all space in the task, whether the windows in it are + // touchable or not. The modal window blocks all touches from the task's + // area. + unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace, + Region.Op.REVERSE_DIFFERENCE); + + final Task task = windowState.getTask(); + if (task != null) { + // If the window is associated with a particular task, we can skip the + // rest of the windows for that task. + skipRemainingWindowsForTasks.add(task.mTaskId); + } else { + // If the window is not associated with a particular task, then it is + // globally modal. In this case we can skip all remaining windows. + unaccountedSpace.setEmpty(); + } + } + } + } + private void computeWindowBoundsInScreen(WindowState windowState, Rect outBounds) { // Get the touchable frame. Region touchableRegion = mTempRegion1; diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 8884615d2fe1..6f8f85f1af26 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -728,9 +728,10 @@ final class ActivityRecord extends ConfigurationContainer { mLastReportedMultiWindowMode = inPictureInPictureMode; final Configuration newConfig = new Configuration(); if (targetStackBounds != null && !targetStackBounds.isEmpty()) { - task.computeResolvedOverrideConfiguration(newConfig, - task.getParent().getConfiguration(), - task.getRequestedOverrideConfiguration()); + newConfig.setTo(task.getRequestedOverrideConfiguration()); + Rect outBounds = newConfig.windowConfiguration.getBounds(); + task.adjustForMinimalTaskDimensions(outBounds, outBounds); + task.computeConfigResourceOverrides(newConfig, task.getParent().getConfiguration()); } schedulePictureInPictureModeChanged(newConfig); scheduleMultiWindowModeChanged(newConfig); @@ -2503,7 +2504,8 @@ final class ActivityRecord extends ConfigurationContainer { return; } - final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null; + final IBinder binder = + (freezeScreenIfNeeded && appToken != null) ? appToken.asBinder() : null; mAppWindowToken.setOrientation(requestedOrientation, binder, this); } @@ -2547,7 +2549,6 @@ final class ActivityRecord extends ConfigurationContainer { // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer. private void updateOverrideConfiguration() { - mTmpConfig.unset(); computeBounds(mTmpBounds); if (mTmpBounds.equals(getRequestedOverrideBounds())) { @@ -2558,8 +2559,10 @@ final class ActivityRecord extends ConfigurationContainer { // Bounds changed...update configuration to match. if (!matchParentBounds()) { - task.computeResolvedOverrideConfiguration(mTmpConfig, - task.getParent().getConfiguration(), getRequestedOverrideConfiguration()); + mTmpConfig.setTo(getRequestedOverrideConfiguration()); + task.computeConfigResourceOverrides(mTmpConfig, task.getParent().getConfiguration()); + } else { + mTmpConfig.unset(); } onRequestedOverrideConfigurationChanged(mTmpConfig); diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index a5341ca11784..2157ef65da4c 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -1714,7 +1714,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (mLetterbox == null) { mLetterbox = new Letterbox(() -> makeChildSurface(null)); } - mLetterbox.layout(getParent().getBounds(), w.getFrameLw()); + getPosition(mTmpPoint); + mLetterbox.layout(getParent().getBounds(), w.getFrameLw(), mTmpPoint); } else if (mLetterbox != null) { mLetterbox.hide(); } diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index ded45c9e3079..650d0be76dc5 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -37,6 +37,7 @@ import static com.android.server.wm.ConfigurationContainerProto.OVERRIDE_CONFIGU import android.annotation.CallSuper; import android.app.WindowConfiguration; import android.content.res.Configuration; +import android.graphics.Point; import android.graphics.Rect; import android.util.proto.ProtoOutputStream; @@ -243,6 +244,14 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { } /** + * Sets {@code out} to the top-left corner of the bounds as returned by {@link #getBounds()}. + */ + public void getPosition(Point out) { + Rect bounds = getBounds(); + out.set(bounds.left, bounds.top); + } + + /** * Returns the bounds requested on this container. These may not be the actual bounds the * container ends up with due to policy constraints. The {@link Rect} handed back is * shared for all calls to this method and should not be modified. diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index fecc8da3d645..740d47240bd0 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -3656,6 +3656,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } + /** @returns the orientation of the display when it's rotation is ROTATION_0. */ + int getNaturalOrientation() { + return mBaseDisplayWidth < mBaseDisplayHeight + ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; + } + void performLayout(boolean initial, boolean updateInputWindows) { if (!isLayoutNeeded()) { return; diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java index 1a2aa2f252e6..33ff19415089 100644 --- a/services/core/java/com/android/server/wm/Letterbox.java +++ b/services/core/java/com/android/server/wm/Letterbox.java @@ -18,6 +18,7 @@ package com.android.server.wm; import static android.view.SurfaceControl.HIDDEN; +import android.graphics.Point; import android.graphics.Rect; import android.view.SurfaceControl; @@ -30,6 +31,7 @@ import java.util.function.Supplier; public class Letterbox { private static final Rect EMPTY_RECT = new Rect(); + private static final Point ZERO_POINT = new Point(0, 0); private final Supplier<SurfaceControl.Builder> mFactory; private final Rect mOuter = new Rect(); @@ -53,14 +55,19 @@ public class Letterbox { * frames will be covered by black color surfaces. * * The caller must use {@link #applySurfaceChanges} to apply the new layout to the surface. - * * @param outer the outer frame of the letterbox (this frame will be black, except the area - * that intersects with the {code inner} frame). - * @param inner the inner frame of the letterbox (this frame will be clear) + * that intersects with the {code inner} frame), in global coordinates + * @param inner the inner frame of the letterbox (this frame will be clear), in global + * coordinates + * @param surfaceOrigin the origin of the surface factory in global coordinates */ - public void layout(Rect outer, Rect inner) { + public void layout(Rect outer, Rect inner, Point surfaceOrigin) { mOuter.set(outer); mInner.set(inner); + mOuter.offset(-surfaceOrigin.x, -surfaceOrigin.y); + mInner.offset(-surfaceOrigin.x, -surfaceOrigin.y); + outer = mOuter; + inner = mInner; mTop.layout(outer.left, outer.top, inner.right, inner.top); mLeft.layout(outer.left, inner.top, inner.left, outer.bottom); @@ -94,7 +101,7 @@ public class Letterbox { * The caller must use {@link #applySurfaceChanges} to apply the new layout to the surface. */ public void hide() { - layout(EMPTY_RECT, EMPTY_RECT); + layout(EMPTY_RECT, EMPTY_RECT, ZERO_POINT); } /** diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index d85fdb03e4a6..937c9d9fc809 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -187,7 +187,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets, Rect outStableInsets, Rect outsets, Rect outBackdropFrame, DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration, - Surface outSurface, InsetsState outInsetsState) { + SurfaceControl outSurfaceControl, InsetsState outInsetsState) { if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from " + Binder.getCallingPid()); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag); @@ -195,7 +195,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { requestedWidth, requestedHeight, viewFlags, flags, frameNumber, outFrame, outOverscanInsets, outContentInsets, outVisibleInsets, outStableInsets, outsets, outBackdropFrame, cutout, - mergedConfiguration, outSurface, outInsetsState); + mergedConfiguration, outSurfaceControl, outInsetsState); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to " + Binder.getCallingPid()); diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java index e3433228f2de..1d56f0489a8d 100644 --- a/services/core/java/com/android/server/wm/TaskRecord.java +++ b/services/core/java/com/android/server/wm/TaskRecord.java @@ -44,6 +44,9 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION; +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static android.content.res.Configuration.ORIENTATION_UNDEFINED; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import static android.provider.Settings.Secure.USER_SETUP_COMPLETE; import static android.view.Display.DEFAULT_DISPLAY; @@ -1259,10 +1262,6 @@ class TaskRecord extends ConfigurationContainer { setFrontOfTask(); } - void addActivityAtBottom(ActivityRecord r) { - addActivityAtIndex(0, r); - } - void addActivityToTop(ActivityRecord r) { addActivityAtIndex(mActivities.size(), r); } @@ -1278,6 +1277,34 @@ class TaskRecord extends ConfigurationContainer { } /** + * Checks if the root activity requires a particular orientation (either by override or + * activityInfo) and returns that. Otherwise, this returns ORIENTATION_UNDEFINED. + */ + private int getRootActivityRequestedOrientation() { + ActivityRecord root = getRootActivity(); + if (getRequestedOverrideConfiguration().orientation != ORIENTATION_UNDEFINED + || root == null) { + return getRequestedOverrideConfiguration().orientation; + } + int rootScreenOrientation = root.getOrientation(); + if (rootScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) { + // NOSENSOR means the display's "natural" orientation, so return that. + ActivityDisplay display = mStack != null ? mStack.getDisplay() : null; + if (display != null && display.mDisplayContent != null) { + return mStack.getDisplay().mDisplayContent.getNaturalOrientation(); + } + } else if (rootScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) { + // LOCKED means the activity's orientation remains unchanged, so return existing value. + return root.getConfiguration().orientation; + } else if (ActivityInfo.isFixedOrientationLandscape(rootScreenOrientation)) { + return ORIENTATION_LANDSCAPE; + } else if (ActivityInfo.isFixedOrientationPortrait(rootScreenOrientation)) { + return ORIENTATION_PORTRAIT; + } + return ORIENTATION_UNDEFINED; + } + + /** * Adds an activity {@param r} at the given {@param index}. The activity {@param r} must either * be in the current task or unparented to any task. */ @@ -1741,7 +1768,7 @@ class TaskRecord extends ConfigurationContainer { updateTaskDescription(); } - private void adjustForMinimalTaskDimensions(Rect bounds, Rect previousBounds) { + void adjustForMinimalTaskDimensions(Rect bounds, Rect previousBounds) { if (bounds == null) { return; } @@ -1853,11 +1880,27 @@ class TaskRecord extends ConfigurationContainer { @Override public void onConfigurationChanged(Configuration newParentConfig) { + // Check if the new configuration supports persistent bounds (eg. is Freeform) and if so + // restore the last recorded non-fullscreen bounds. + final boolean prevPersistTaskBounds = getWindowConfiguration().persistTaskBounds(); + final boolean nextPersistTaskBounds = + getRequestedOverrideConfiguration().windowConfiguration.persistTaskBounds() + || newParentConfig.windowConfiguration.persistTaskBounds(); + if (!prevPersistTaskBounds && nextPersistTaskBounds + && mLastNonFullscreenBounds != null && !mLastNonFullscreenBounds.isEmpty()) { + // Bypass onRequestedOverrideConfigurationChanged here to avoid infinite loop. + getRequestedOverrideConfiguration().windowConfiguration + .setBounds(mLastNonFullscreenBounds); + } + final boolean wasInMultiWindowMode = inMultiWindowMode(); super.onConfigurationChanged(newParentConfig); if (wasInMultiWindowMode != inMultiWindowMode()) { mService.mStackSupervisor.scheduleUpdateMultiWindowMode(this); } + + // If the configuration supports persistent bounds (eg. Freeform), keep track of the + // current (non-fullscreen) bounds for persistence. if (getWindowConfiguration().persistTaskBounds()) { final Rect currentBounds = getRequestedOverrideBounds(); if (!currentBounds.isEmpty()) { @@ -2047,7 +2090,7 @@ class TaskRecord extends ConfigurationContainer { * configuring an "inherit-bounds" window which means that all configuration settings would * just be inherited from the parent configuration. **/ - void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, @NonNull Rect bounds, + void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, @NonNull Configuration parentConfig) { int windowingMode = inOutConfig.windowConfiguration.getWindowingMode(); if (windowingMode == WINDOWING_MODE_UNDEFINED) { @@ -2060,6 +2103,7 @@ class TaskRecord extends ConfigurationContainer { } density *= DisplayMetrics.DENSITY_DEFAULT_SCALE; + final Rect bounds = inOutConfig.windowConfiguration.getBounds(); Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds(); if (outAppBounds == null || outAppBounds.isEmpty()) { inOutConfig.windowConfiguration.setAppBounds(bounds); @@ -2107,13 +2151,14 @@ class TaskRecord extends ConfigurationContainer { // Iterating across all screen orientations, and return the minimum of the task // width taking into account that the bounds might change because the snap // algorithm snaps to a different value - getSmallestScreenWidthDpForDockedBounds(bounds); + inOutConfig.smallestScreenWidthDp = + getSmallestScreenWidthDpForDockedBounds(bounds); } // otherwise, it will just inherit } } - if (inOutConfig.orientation == Configuration.ORIENTATION_UNDEFINED) { + if (inOutConfig.orientation == ORIENTATION_UNDEFINED) { inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp) ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE; } @@ -2134,36 +2179,56 @@ class TaskRecord extends ConfigurationContainer { } } - // TODO(b/113900640): remove this once ActivityRecord is changed to not need it anymore. - void computeResolvedOverrideConfiguration(Configuration inOutConfig, Configuration parentConfig, - Configuration overrideConfig) { - // Save previous bounds because adjustForMinimalTaskDimensions uses that to determine if it - // changes left bound vs. right bound, or top bound vs. bottom bound. - mTmpBounds.set(inOutConfig.windowConfiguration.getBounds()); - - inOutConfig.setTo(overrideConfig); - - Rect outOverrideBounds = inOutConfig.windowConfiguration.getBounds(); - if (outOverrideBounds != null && !outOverrideBounds.isEmpty()) { - adjustForMinimalTaskDimensions(outOverrideBounds, mTmpBounds); - - int windowingMode = overrideConfig.windowConfiguration.getWindowingMode(); - if (windowingMode == WINDOWING_MODE_UNDEFINED) { - windowingMode = parentConfig.windowConfiguration.getWindowingMode(); - } - if (windowingMode == WINDOWING_MODE_FREEFORM) { - // by policy, make sure the window remains within parent - fitWithinBounds(outOverrideBounds, parentConfig.windowConfiguration.getBounds()); + @Override + void resolveOverrideConfiguration(Configuration newParentConfig) { + mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds()); + super.resolveOverrideConfiguration(newParentConfig); + int windowingMode = + getRequestedOverrideConfiguration().windowConfiguration.getWindowingMode(); + if (windowingMode == WINDOWING_MODE_UNDEFINED) { + windowingMode = newParentConfig.windowConfiguration.getWindowingMode(); + } + Rect outOverrideBounds = + getResolvedOverrideConfiguration().windowConfiguration.getBounds(); + + if (windowingMode == WINDOWING_MODE_FULLSCREEN) { + // In FULLSCREEN mode, always start with empty bounds to indicate "fill parent" + outOverrideBounds.setEmpty(); + + // If the task or its root activity require a different orientation, make it fit the + // available bounds by scaling down its bounds. + int forcedOrientation = getRootActivityRequestedOrientation(); + if (forcedOrientation != ORIENTATION_UNDEFINED + && forcedOrientation != newParentConfig.orientation) { + final Rect parentBounds = newParentConfig.windowConfiguration.getBounds(); + final int parentWidth = parentBounds.width(); + final int parentHeight = parentBounds.height(); + final float aspect = ((float) parentHeight) / parentWidth; + if (forcedOrientation == ORIENTATION_LANDSCAPE) { + final int height = (int) (parentWidth / aspect); + final int top = parentBounds.centerY() - height / 2; + outOverrideBounds.set( + parentBounds.left, top, parentBounds.right, top + height); + } else { + final int width = (int) (parentHeight * aspect); + final int left = parentBounds.centerX() - width / 2; + outOverrideBounds.set( + left, parentBounds.top, left + width, parentBounds.bottom); + } } + } - computeConfigResourceOverrides(inOutConfig, outOverrideBounds, parentConfig); + if (outOverrideBounds.isEmpty()) { + // If the task fills the parent, just inherit all the other configs from parent. + return; } - } - @Override - void resolveOverrideConfiguration(Configuration newParentConfig) { - computeResolvedOverrideConfiguration(getResolvedOverrideConfiguration(), newParentConfig, - getRequestedOverrideConfiguration()); + adjustForMinimalTaskDimensions(outOverrideBounds, mTmpBounds); + if (windowingMode == WINDOWING_MODE_FREEFORM) { + // by policy, make sure the window remains within parent somewhere + fitWithinBounds(outOverrideBounds, newParentConfig.windowConfiguration.getBounds()); + } + computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig); } Rect updateOverrideConfigurationFromLaunchBounds() { diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index 9a56606aee7f..2d3e3aee4b7f 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -111,6 +111,7 @@ class TaskSnapshotSurface implements StartingSurface { private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s"; private final Window mWindow; private final Surface mSurface; + private SurfaceControl mSurfaceControl; private SurfaceControl mChildSurfaceControl; private final IWindowSession mSession; private final WindowManagerService mService; @@ -136,7 +137,7 @@ class TaskSnapshotSurface implements StartingSurface { final Window window = new Window(); final IWindowSession session = WindowManagerGlobal.getWindowSession(); window.setSession(session); - final Surface surface = new Surface(); + final SurfaceControl surfaceControl = new SurfaceControl(); final Rect tmpRect = new Rect(); final DisplayCutout.ParcelableWrapper tmpCutout = new DisplayCutout.ParcelableWrapper(); final Rect tmpFrame = new Rect(); @@ -213,14 +214,14 @@ class TaskSnapshotSurface implements StartingSurface { // Local call. } final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window, - surface, snapshot, layoutParams.getTitle(), backgroundColor, statusBarColor, + surfaceControl, snapshot, layoutParams.getTitle(), backgroundColor, statusBarColor, navigationBarColor, sysUiVis, windowFlags, windowPrivateFlags, taskBounds, currentOrientation); window.setOuter(snapshotSurface); try { session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1, tmpFrame, tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect, - tmpCutout, tmpMergedConfiguration, surface, mTmpInsetsState); + tmpCutout, tmpMergedConfiguration, surfaceControl, mTmpInsetsState); } catch (RemoteException e) { // Local call. } @@ -230,15 +231,16 @@ class TaskSnapshotSurface implements StartingSurface { } @VisibleForTesting - TaskSnapshotSurface(WindowManagerService service, Window window, Surface surface, + TaskSnapshotSurface(WindowManagerService service, Window window, SurfaceControl surfaceControl, TaskSnapshot snapshot, CharSequence title, int backgroundColor, int statusBarColor, int navigationBarColor, int sysUiVis, int windowFlags, int windowPrivateFlags, Rect taskBounds, int currentOrientation) { mService = service; + mSurface = new Surface(); mHandler = new Handler(mService.mH.getLooper()); mSession = WindowManagerGlobal.getWindowSession(); mWindow = window; - mSurface = surface; + mSurfaceControl = surfaceControl; mSnapshot = snapshot; mTitle = title; mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE); @@ -281,6 +283,8 @@ class TaskSnapshotSurface implements StartingSurface { private void drawSnapshot() { final GraphicBuffer buffer = mSnapshot.getSnapshot(); + mSurface.copyFrom(mSurfaceControl); + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Drawing snapshot surface sizeMismatch=" + mSizeMismatch); if (mSizeMismatch) { @@ -310,13 +314,14 @@ class TaskSnapshotSurface implements StartingSurface { if (!mSurface.isValid()) { throw new IllegalStateException("mSurface does not hold a valid surface."); } - final SurfaceSession session = new SurfaceSession(mSurface); + final SurfaceSession session = new SurfaceSession(); // Keep a reference to it such that it doesn't get destroyed when finalized. mChildSurfaceControl = new SurfaceControl.Builder(session) .setName(mTitle + " - task-snapshot-surface") .setBufferSize(buffer.getWidth(), buffer.getHeight()) .setFormat(buffer.getFormat()) + .setParent(mSurfaceControl) .build(); Surface surface = new Surface(); surface.copyFrom(mChildSurfaceControl); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index f5f55e2ebf73..91aac7e0e8aa 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1874,7 +1874,7 @@ public class WindowManagerService extends IWindowManager.Stub long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame, DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration, - Surface outSurface, InsetsState outInsetsState) { + SurfaceControl outSurfaceControl, InsetsState outInsetsState) { int result = 0; boolean configChanged; final boolean hasStatusBarPermission = @@ -2047,7 +2047,7 @@ public class WindowManagerService extends IWindowManager.Stub result = win.relayoutVisibleWindow(result, attrChanges, oldVisibility); try { - result = createSurfaceControl(outSurface, result, win, winAnimator); + result = createSurfaceControl(outSurfaceControl, result, win, winAnimator); } catch (Exception e) { displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/); @@ -2078,7 +2078,7 @@ public class WindowManagerService extends IWindowManager.Stub // handled yet, or it might want to draw a last frame. If we already have a // surface, let the client use that, but don't create new surface at this point. Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: getSurface"); - winAnimator.mSurfaceController.getSurface(outSurface); + winAnimator.mSurfaceController.getSurfaceControl(outSurfaceControl); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } else { if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win); @@ -2086,7 +2086,7 @@ public class WindowManagerService extends IWindowManager.Stub try { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmReleaseOutSurface_" + win.mAttrs.getTitle()); - outSurface.release(); + outSurfaceControl.release(); } finally { Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } @@ -2182,7 +2182,7 @@ public class WindowManagerService extends IWindowManager.Stub + ", requestedHeight=" + requestedHeight + ", viewVisibility=" + viewVisibility + "\nRelayout returning frame=" + outFrame - + ", surface=" + outSurface); + + ", surface=" + outSurfaceControl); if (localLOGV || DEBUG_FOCUS) Slog.v( TAG_WM, "Relayout of " + win + ": focusMayChange=" + focusMayChange); @@ -2252,7 +2252,7 @@ public class WindowManagerService extends IWindowManager.Stub return focusMayChange; } - private int createSurfaceControl(Surface outSurface, int result, WindowState win, + private int createSurfaceControl(SurfaceControl outSurfaceControl, int result, WindowState win, WindowStateAnimator winAnimator) { if (!win.mHasSurface) { result |= RELAYOUT_RES_SURFACE_CHANGED; @@ -2266,13 +2266,13 @@ public class WindowManagerService extends IWindowManager.Stub Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } if (surfaceController != null) { - surfaceController.getSurface(outSurface); - if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " OUT SURFACE " + outSurface + ": copied"); + surfaceController.getSurfaceControl(outSurfaceControl); + if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " OUT SURFACE " + outSurfaceControl + ": copied"); } else { // For some reason there isn't a surface. Clear the // caller's object so they see the same state. Slog.w(TAG_WM, "Failed to create surface control for " + win); - outSurface.release(); + outSurfaceControl.release(); } return result; @@ -6166,7 +6166,7 @@ public class WindowManagerService extends IWindowManager.Stub dumpWindowsLocked(pw, true, null); } return; - } else if ("all".equals(cmd) || "a".equals(cmd)) { + } else if ("all".equals(cmd)) { synchronized (mGlobalLock) { dumpWindowsLocked(pw, true, null); } diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index ce627e23e6ee..c2a8e7efb5a5 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -478,8 +478,8 @@ class WindowSurfaceController { return mSurfaceControl.getHandle(); } - void getSurface(Surface outSurface) { - outSurface.copyFrom(mSurfaceControl); + void getSurfaceControl(SurfaceControl outSurfaceControl) { + outSurfaceControl.copyFrom(mSurfaceControl); } int getLayer() { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java index 05912a5e3776..de5dd1749830 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java @@ -66,6 +66,9 @@ class AbUpdateInstaller extends UpdateInstaller { map.put( DOWNLOAD_STATE_INITIALIZATION_ERROR, InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION); + map.put( + UpdateEngine.ErrorCodeConstants.PAYLOAD_TIMESTAMP_ERROR, + InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION); // Error constants corresponding to errors related to bad update file. map.put( diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 781e1c4a41d9..7f6895a4cf83 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -4990,26 +4990,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private boolean resetPasswordInternal(String password, long tokenHandle, byte[] token, int flags, int callingUid, int userHandle) { int quality; - final int realQuality; synchronized (getLockObject()) { quality = getPasswordQuality(null, userHandle, /* parent */ false); if (quality == DevicePolicyManager.PASSWORD_QUALITY_MANAGED) { quality = PASSWORD_QUALITY_UNSPECIFIED; } final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password); - realQuality = metrics.quality; - if (quality != PASSWORD_QUALITY_UNSPECIFIED) { - - if (realQuality < quality - && quality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) { - Slog.w(LOG_TAG, "resetPassword: password quality 0x" - + Integer.toHexString(realQuality) - + " does not meet required quality 0x" - + Integer.toHexString(quality)); - return false; - } - quality = Math.max(realQuality, quality); + final int realQuality = metrics.quality; + if (realQuality < quality + && quality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) { + Slog.w(LOG_TAG, "resetPassword: password quality 0x" + + Integer.toHexString(realQuality) + + " does not meet required quality 0x" + + Integer.toHexString(quality)); + return false; } + quality = Math.max(realQuality, quality); int length = getPasswordMinimumLength(null, userHandle, /* parent */ false); if (password.length() < length) { Slog.w(LOG_TAG, "resetPassword: password length " + password.length() @@ -5086,7 +5082,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { try { if (token == null) { if (!TextUtils.isEmpty(password)) { - mLockPatternUtils.saveLockPassword(password, null, realQuality, userHandle); + mLockPatternUtils.saveLockPassword(password, null, quality, userHandle); } else { mLockPatternUtils.clearLock(null, userHandle); } @@ -5095,7 +5091,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { result = mLockPatternUtils.setLockCredentialWithToken(password, TextUtils.isEmpty(password) ? LockPatternUtils.CREDENTIAL_TYPE_NONE : LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, - realQuality, tokenHandle, token, userHandle); + quality, tokenHandle, token, userHandle); } boolean requireEntry = (flags & DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0; if (requireEntry) { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index be09aea65073..4326c39c43a2 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -145,6 +145,7 @@ import com.android.server.webkit.WebViewUpdateService; import com.android.server.wm.ActivityTaskManagerService; import com.android.server.wm.WindowManagerGlobalLock; import com.android.server.wm.WindowManagerService; +import com.google.android.startop.iorap.IorapForwardingService; import dalvik.system.VMRuntime; @@ -1007,10 +1008,13 @@ public final class SystemServer { mSystemServiceManager.startService(PinnerService.class); traceEnd(); + traceBeginAndSlog("IorapForwardingService"); + mSystemServiceManager.startService(IorapForwardingService.class); + traceEnd(); + traceBeginAndSlog("SignedConfigService"); SignedConfigService.registerUpdateReceiver(mSystemContext); traceEnd(); - } catch (RuntimeException e) { Slog.e("System", "******************************************"); Slog.e("System", "************ Failure starting core service", e); diff --git a/services/net/java/android/net/dhcp/DhcpLease.java b/services/net/java/android/net/dhcp/DhcpLease.java index 6cdd2aa8579d..6849cfadc22a 100644 --- a/services/net/java/android/net/dhcp/DhcpLease.java +++ b/services/net/java/android/net/dhcp/DhcpLease.java @@ -58,6 +58,11 @@ public class DhcpLease { mHostname = hostname; } + /** + * Get the clientId associated with this lease, if any. + * + * <p>If the lease is not associated to a clientId, this returns null. + */ @Nullable public byte[] getClientId() { if (mClientId == null) { @@ -97,6 +102,11 @@ public class DhcpLease { (hostname == null ? mHostname : hostname)); } + /** + * Determine whether this lease matches a client with the specified parameters. + * @param clientId clientId of the client if any, or null otherwise. + * @param hwAddr Hardware address of the client. + */ public boolean matchesClient(@Nullable byte[] clientId, @NonNull MacAddress hwAddr) { if (mClientId != null) { return Arrays.equals(mClientId, clientId); @@ -110,7 +120,7 @@ public class DhcpLease { if (!(obj instanceof DhcpLease)) { return false; } - final DhcpLease other = (DhcpLease)obj; + final DhcpLease other = (DhcpLease) obj; return Arrays.equals(mClientId, other.mClientId) && mHwAddr.equals(other.mHwAddr) && mNetAddr.equals(other.mNetAddr) diff --git a/services/net/java/android/net/dhcp/DhcpLeaseRepository.java b/services/net/java/android/net/dhcp/DhcpLeaseRepository.java index 2dda42124c75..b3d0512ba447 100644 --- a/services/net/java/android/net/dhcp/DhcpLeaseRepository.java +++ b/services/net/java/android/net/dhcp/DhcpLeaseRepository.java @@ -29,8 +29,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.net.IpPrefix; import android.net.MacAddress; -import android.net.util.SharedLog; import android.net.dhcp.DhcpServer.Clock; +import android.net.util.SharedLog; import android.util.ArrayMap; import java.net.Inet4Address; @@ -117,7 +117,7 @@ class DhcpLeaseRepository { */ private final LinkedHashMap<Inet4Address, Long> mDeclinedAddrs = new LinkedHashMap<>(); - public DhcpLeaseRepository(@NonNull IpPrefix prefix, @NonNull Set<Inet4Address> reservedAddrs, + DhcpLeaseRepository(@NonNull IpPrefix prefix, @NonNull Set<Inet4Address> reservedAddrs, long leaseTimeMs, @NonNull SharedLog log, @NonNull Clock clock) { updateParams(prefix, reservedAddrs, leaseTimeMs); mLog = log; @@ -250,8 +250,8 @@ class DhcpLeaseRepository { // reqAddr null (RENEWING/REBINDING): client renewing its own lease for clientAddr. // reqAddr set with sid not set (INIT-REBOOT): client verifying configuration. // In both cases, throw if clientAddr or reqAddr does not match the known lease. - throw new InvalidAddressException("Incorrect address for client in " + - (reqAddr != null ? "INIT-REBOOT" : "RENEWING/REBINDING")); + throw new InvalidAddressException("Incorrect address for client in " + + (reqAddr != null ? "INIT-REBOOT" : "RENEWING/REBINDING")); } } diff --git a/services/net/java/android/net/dhcp/DhcpPacketListener.java b/services/net/java/android/net/dhcp/DhcpPacketListener.java index 6f620c5ce30e..dce8b619494e 100644 --- a/services/net/java/android/net/dhcp/DhcpPacketListener.java +++ b/services/net/java/android/net/dhcp/DhcpPacketListener.java @@ -32,32 +32,32 @@ import java.net.InetSocketAddress; */ abstract class DhcpPacketListener extends FdEventsReader<DhcpPacketListener.Payload> { static final class Payload { - final byte[] bytes = new byte[DhcpPacket.MAX_LENGTH]; - Inet4Address srcAddr; - int srcPort; + protected final byte[] mBytes = new byte[DhcpPacket.MAX_LENGTH]; + protected Inet4Address mSrcAddr; + protected int mSrcPort; } - public DhcpPacketListener(@NonNull Handler handler) { + DhcpPacketListener(@NonNull Handler handler) { super(handler, new Payload()); } @Override protected int recvBufSize(@NonNull Payload buffer) { - return buffer.bytes.length; + return buffer.mBytes.length; } @Override protected final void handlePacket(@NonNull Payload recvbuf, int length) { - if (recvbuf.srcAddr == null) { + if (recvbuf.mSrcAddr == null) { return; } try { - final DhcpPacket packet = DhcpPacket.decodeFullPacket(recvbuf.bytes, length, + final DhcpPacket packet = DhcpPacket.decodeFullPacket(recvbuf.mBytes, length, DhcpPacket.ENCAP_BOOTP); - onReceive(packet, recvbuf.srcAddr, recvbuf.srcPort); + onReceive(packet, recvbuf.mSrcAddr, recvbuf.mSrcPort); } catch (DhcpPacket.ParseException e) { - logParseError(recvbuf.bytes, length, e); + logParseError(recvbuf.mBytes, length, e); } } @@ -66,11 +66,11 @@ abstract class DhcpPacketListener extends FdEventsReader<DhcpPacketListener.Payl throws Exception { final InetSocketAddress addr = new InetSocketAddress(); final int read = Os.recvfrom( - fd, packetBuffer.bytes, 0, packetBuffer.bytes.length, 0 /* flags */, addr); + fd, packetBuffer.mBytes, 0, packetBuffer.mBytes.length, 0 /* flags */, addr); // Buffers with null srcAddr will be dropped in handlePacket() - packetBuffer.srcAddr = inet4AddrOrNull(addr); - packetBuffer.srcPort = addr.getPort(); + packetBuffer.mSrcAddr = inet4AddrOrNull(addr); + packetBuffer.mSrcPort = addr.getPort(); return read; } diff --git a/services/net/java/android/net/dhcp/DhcpServer.java b/services/net/java/android/net/dhcp/DhcpServer.java index 35d29e75c0e4..641bba2ed306 100644 --- a/services/net/java/android/net/dhcp/DhcpServer.java +++ b/services/net/java/android/net/dhcp/DhcpServer.java @@ -101,6 +101,13 @@ public class DhcpServer { @NonNull private DhcpServingParams mServingParams; + /** + * Clock to be used by DhcpServer to track time for lease expiration. + * + * <p>The clock should track time as may be measured by clients obtaining a lease. It does not + * need to be monotonous across restarts of the server as long as leases are cleared when the + * server is stopped. + */ public static class Clock { /** * @see SystemClock#elapsedRealtime() @@ -110,13 +117,43 @@ public class DhcpServer { } } + /** + * Dependencies for the DhcpServer. Useful to be mocked in tests. + */ public interface Dependencies { + /** + * Send a packet to the specified datagram socket. + * + * @param fd File descriptor of the socket. + * @param buffer Data to be sent. + * @param dst Destination address of the packet. + */ void sendPacket(@NonNull FileDescriptor fd, @NonNull ByteBuffer buffer, @NonNull InetAddress dst) throws ErrnoException, IOException; + + /** + * Create a DhcpLeaseRepository for the server. + * @param servingParams Parameters used to serve DHCP requests. + * @param log Log to be used by the repository. + * @param clock Clock that the repository must use to track time. + */ DhcpLeaseRepository makeLeaseRepository(@NonNull DhcpServingParams servingParams, @NonNull SharedLog log, @NonNull Clock clock); + + /** + * Create a packet listener that will send packets to be processed. + */ DhcpPacketListener makePacketListener(); + + /** + * Create a clock that the server will use to track time. + */ Clock makeClock(); + + /** + * Add an entry to the ARP cache table. + * @param fd Datagram socket file descriptor that must use the new entry. + */ void addArpEntry(@NonNull Inet4Address ipv4Addr, @NonNull MacAddress ethAddr, @NonNull String ifname, @NonNull FileDescriptor fd) throws IOException; } @@ -134,7 +171,7 @@ public class DhcpServer { return new DhcpLeaseRepository( DhcpServingParams.makeIpPrefix(servingParams.serverAddr), servingParams.excludedAddrs, - servingParams.dhcpLeaseTimeSecs*1000, log.forSubComponent(REPO_TAG), clock); + servingParams.dhcpLeaseTimeSecs * 1000, log.forSubComponent(REPO_TAG), clock); } @Override @@ -212,7 +249,7 @@ public class DhcpServer { } private class ServerHandler extends Handler { - public ServerHandler(@NonNull Looper looper) { + ServerHandler(@NonNull Looper looper) { super(looper); } @@ -496,22 +533,24 @@ public class DhcpServer { } private class PacketListener extends DhcpPacketListener { - public PacketListener() { + PacketListener() { super(mHandler); } @Override - protected void onReceive(DhcpPacket packet, Inet4Address srcAddr, int srcPort) { + protected void onReceive(@NonNull DhcpPacket packet, @NonNull Inet4Address srcAddr, + int srcPort) { processPacket(packet, srcPort); } @Override - protected void logError(String msg, Exception e) { + protected void logError(@NonNull String msg, Exception e) { mLog.e("Error receiving packet: " + msg, e); } @Override - protected void logParseError(byte[] packet, int length, DhcpPacket.ParseException e) { + protected void logParseError(@NonNull byte[] packet, int length, + @NonNull DhcpPacket.ParseException e) { mLog.e("Error parsing packet", e); } diff --git a/services/net/java/android/net/dhcp/DhcpServingParams.java b/services/net/java/android/net/dhcp/DhcpServingParams.java index df15ba1c5507..2780814a2f33 100644 --- a/services/net/java/android/net/dhcp/DhcpServingParams.java +++ b/services/net/java/android/net/dhcp/DhcpServingParams.java @@ -17,6 +17,7 @@ package android.net.dhcp; import static android.net.NetworkUtils.getPrefixMaskAsInet4Address; +import static android.net.NetworkUtils.intToInet4AddressHTH; import static android.net.dhcp.DhcpPacket.INFINITE_LEASE; import static android.net.util.NetworkConstants.IPV4_MAX_MTU; import static android.net.util.NetworkConstants.IPV4_MIN_MTU; @@ -24,6 +25,7 @@ import static android.net.util.NetworkConstants.IPV4_MIN_MTU; import static java.lang.Integer.toUnsignedLong; import android.annotation.NonNull; +import android.annotation.Nullable; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.NetworkUtils; @@ -103,6 +105,37 @@ public class DhcpServingParams { this.metered = metered; } + /** + * Create parameters from a stable AIDL-compatible parcel. + */ + public static DhcpServingParams fromParcelableObject(@NonNull DhcpServingParamsParcel parcel) + throws InvalidParameterException { + final LinkAddress serverAddr = new LinkAddress( + intToInet4AddressHTH(parcel.serverAddr), + parcel.serverAddrPrefixLength); + return new Builder() + .setServerAddr(serverAddr) + .setDefaultRouters(toInet4AddressSet(parcel.defaultRouters)) + .setDnsServers(toInet4AddressSet(parcel.dnsServers)) + .setExcludedAddrs(toInet4AddressSet(parcel.excludedAddrs)) + .setDhcpLeaseTimeSecs(parcel.dhcpLeaseTimeSecs) + .setLinkMtu(parcel.linkMtu) + .setMetered(parcel.metered) + .build(); + } + + private static Set<Inet4Address> toInet4AddressSet(@Nullable int[] addrs) { + if (addrs == null) { + return new HashSet<>(0); + } + + final HashSet<Inet4Address> res = new HashSet<>(); + for (int addr : addrs) { + res.add(intToInet4AddressHTH(addr)); + } + return res; + } + @NonNull public Inet4Address getServerInet4Addr() { return (Inet4Address) serverAddr.getAddress(); @@ -134,13 +167,13 @@ public class DhcpServingParams { * of the parameters. */ public static class Builder { - private LinkAddress serverAddr; - private Set<Inet4Address> defaultRouters; - private Set<Inet4Address> dnsServers; - private Set<Inet4Address> excludedAddrs; - private long dhcpLeaseTimeSecs; - private int linkMtu = MTU_UNSET; - private boolean metered; + private LinkAddress mServerAddr; + private Set<Inet4Address> mDefaultRouters; + private Set<Inet4Address> mDnsServers; + private Set<Inet4Address> mExcludedAddrs; + private long mDhcpLeaseTimeSecs; + private int mLinkMtu = MTU_UNSET; + private boolean mMetered; /** * Set the server address and served prefix for the DHCP server. @@ -148,7 +181,7 @@ public class DhcpServingParams { * <p>This parameter is required. */ public Builder setServerAddr(@NonNull LinkAddress serverAddr) { - this.serverAddr = serverAddr; + this.mServerAddr = serverAddr; return this; } @@ -159,7 +192,7 @@ public class DhcpServingParams { * always be set explicitly before building the {@link DhcpServingParams}. */ public Builder setDefaultRouters(@NonNull Set<Inet4Address> defaultRouters) { - this.defaultRouters = defaultRouters; + this.mDefaultRouters = defaultRouters; return this; } @@ -189,7 +222,7 @@ public class DhcpServingParams { * {@link DhcpServingParams}. */ public Builder setDnsServers(@NonNull Set<Inet4Address> dnsServers) { - this.dnsServers = dnsServers; + this.mDnsServers = dnsServers; return this; } @@ -219,7 +252,7 @@ public class DhcpServingParams { * and do not need to be set here. */ public Builder setExcludedAddrs(@NonNull Set<Inet4Address> excludedAddrs) { - this.excludedAddrs = excludedAddrs; + this.mExcludedAddrs = excludedAddrs; return this; } @@ -239,7 +272,7 @@ public class DhcpServingParams { * <p>This parameter is required. */ public Builder setDhcpLeaseTimeSecs(long dhcpLeaseTimeSecs) { - this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs; + this.mDhcpLeaseTimeSecs = dhcpLeaseTimeSecs; return this; } @@ -250,7 +283,7 @@ public class DhcpServingParams { * is optional and defaults to {@link #MTU_UNSET}. */ public Builder setLinkMtu(int linkMtu) { - this.linkMtu = linkMtu; + this.mLinkMtu = linkMtu; return this; } @@ -260,7 +293,7 @@ public class DhcpServingParams { * <p>If not set, the default value is false. */ public Builder setMetered(boolean metered) { - this.metered = metered; + this.mMetered = metered; return this; } @@ -274,54 +307,57 @@ public class DhcpServingParams { */ @NonNull public DhcpServingParams build() throws InvalidParameterException { - if (serverAddr == null) { + if (mServerAddr == null) { throw new InvalidParameterException("Missing serverAddr"); } - if (defaultRouters == null) { + if (mDefaultRouters == null) { throw new InvalidParameterException("Missing defaultRouters"); } - if (dnsServers == null) { + if (mDnsServers == null) { // Empty set is OK, but enforce explicitly setting it throw new InvalidParameterException("Missing dnsServers"); } - if (dhcpLeaseTimeSecs <= 0 || dhcpLeaseTimeSecs > toUnsignedLong(INFINITE_LEASE)) { - throw new InvalidParameterException("Invalid lease time: " + dhcpLeaseTimeSecs); + if (mDhcpLeaseTimeSecs <= 0 || mDhcpLeaseTimeSecs > toUnsignedLong(INFINITE_LEASE)) { + throw new InvalidParameterException("Invalid lease time: " + mDhcpLeaseTimeSecs); } - if (linkMtu != MTU_UNSET && (linkMtu < IPV4_MIN_MTU || linkMtu > IPV4_MAX_MTU)) { - throw new InvalidParameterException("Invalid link MTU: " + linkMtu); + if (mLinkMtu != MTU_UNSET && (mLinkMtu < IPV4_MIN_MTU || mLinkMtu > IPV4_MAX_MTU)) { + throw new InvalidParameterException("Invalid link MTU: " + mLinkMtu); } - if (!serverAddr.isIPv4()) { + if (!mServerAddr.isIPv4()) { throw new InvalidParameterException("serverAddr must be IPv4"); } - if (serverAddr.getPrefixLength() < MIN_PREFIX_LENGTH - || serverAddr.getPrefixLength() > MAX_PREFIX_LENGTH) { + if (mServerAddr.getPrefixLength() < MIN_PREFIX_LENGTH + || mServerAddr.getPrefixLength() > MAX_PREFIX_LENGTH) { throw new InvalidParameterException("Prefix length is not in supported range"); } - final IpPrefix prefix = makeIpPrefix(serverAddr); - for (Inet4Address addr : defaultRouters) { + final IpPrefix prefix = makeIpPrefix(mServerAddr); + for (Inet4Address addr : mDefaultRouters) { if (!prefix.contains(addr)) { throw new InvalidParameterException(String.format( - "Default router %s is not in server prefix %s", addr, serverAddr)); + "Default router %s is not in server prefix %s", addr, mServerAddr)); } } final Set<Inet4Address> excl = new HashSet<>(); - if (excludedAddrs != null) { - excl.addAll(excludedAddrs); + if (mExcludedAddrs != null) { + excl.addAll(mExcludedAddrs); } - excl.add((Inet4Address) serverAddr.getAddress()); - excl.addAll(defaultRouters); - excl.addAll(dnsServers); + excl.add((Inet4Address) mServerAddr.getAddress()); + excl.addAll(mDefaultRouters); + excl.addAll(mDnsServers); - return new DhcpServingParams(serverAddr, - Collections.unmodifiableSet(new HashSet<>(defaultRouters)), - Collections.unmodifiableSet(new HashSet<>(dnsServers)), + return new DhcpServingParams(mServerAddr, + Collections.unmodifiableSet(new HashSet<>(mDefaultRouters)), + Collections.unmodifiableSet(new HashSet<>(mDnsServers)), Collections.unmodifiableSet(excl), - dhcpLeaseTimeSecs, linkMtu, metered); + mDhcpLeaseTimeSecs, mLinkMtu, mMetered); } } + /** + * Utility method to create an IpPrefix with the address and prefix length of a LinkAddress. + */ @NonNull static IpPrefix makeIpPrefix(@NonNull LinkAddress addr) { return new IpPrefix(addr.getAddress(), addr.getPrefixLength()); diff --git a/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java b/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java new file mode 100644 index 000000000000..f068c3ac16e2 --- /dev/null +++ b/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2018 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.net.dhcp; + +import static android.net.NetworkUtils.inet4AddressToIntHTH; + +import android.annotation.NonNull; +import android.net.LinkAddress; + +import com.google.android.collect.Sets; + +import java.net.Inet4Address; +import java.util.Collection; +import java.util.Set; + +/** + * Subclass of {@link DhcpServingParamsParcel} with additional utility methods for building. + * + * <p>This utility class does not check for validity of the parameters: invalid parameters are + * reported by the receiving module when unparceling the parcel. + * + * @see DhcpServingParams + * @hide + */ +public class DhcpServingParamsParcelExt extends DhcpServingParamsParcel { + public static final int MTU_UNSET = 0; + + /** + * Set the server address and served prefix for the DHCP server. + * + * <p>This parameter is required. + */ + public DhcpServingParamsParcelExt setServerAddr(@NonNull LinkAddress serverAddr) { + this.serverAddr = inet4AddressToIntHTH((Inet4Address) serverAddr.getAddress()); + this.serverAddrPrefixLength = serverAddr.getPrefixLength(); + return this; + } + + /** + * Set the default routers to be advertised to DHCP clients. + * + * <p>Each router must be inside the served prefix. This may be an empty set, but it must + * always be set explicitly. + */ + public DhcpServingParamsParcelExt setDefaultRouters(@NonNull Set<Inet4Address> defaultRouters) { + this.defaultRouters = toIntArray(defaultRouters); + return this; + } + + /** + * Set the default routers to be advertised to DHCP clients. + * + * <p>Each router must be inside the served prefix. This may be an empty list of routers, + * but it must always be set explicitly. + */ + public DhcpServingParamsParcelExt setDefaultRouters(@NonNull Inet4Address... defaultRouters) { + return setDefaultRouters(Sets.newArraySet(defaultRouters)); + } + + /** + * Convenience method to build the parameters with no default router. + * + * <p>Equivalent to calling {@link #setDefaultRouters(Inet4Address...)} with no address. + */ + public DhcpServingParamsParcelExt setNoDefaultRouter() { + return setDefaultRouters(); + } + + /** + * Set the DNS servers to be advertised to DHCP clients. + * + * <p>This may be an empty set, but it must always be set explicitly. + */ + public DhcpServingParamsParcelExt setDnsServers(@NonNull Set<Inet4Address> dnsServers) { + this.dnsServers = toIntArray(dnsServers); + return this; + } + + /** + * Set the DNS servers to be advertised to DHCP clients. + * + * <p>This may be an empty list of servers, but it must always be set explicitly. + */ + public DhcpServingParamsParcelExt setDnsServers(@NonNull Inet4Address... dnsServers) { + return setDnsServers(Sets.newArraySet(dnsServers)); + } + + /** + * Convenience method to build the parameters with no DNS server. + * + * <p>Equivalent to calling {@link #setDnsServers(Inet4Address...)} with no address. + */ + public DhcpServingParamsParcelExt setNoDnsServer() { + return setDnsServers(); + } + + /** + * Set excluded addresses that the DHCP server is not allowed to assign to clients. + * + * <p>This parameter is optional. DNS servers and default routers are always excluded + * and do not need to be set here. + */ + public DhcpServingParamsParcelExt setExcludedAddrs(@NonNull Set<Inet4Address> excludedAddrs) { + this.excludedAddrs = toIntArray(excludedAddrs); + return this; + } + + /** + * Set excluded addresses that the DHCP server is not allowed to assign to clients. + * + * <p>This parameter is optional. DNS servers and default routers are always excluded + * and do not need to be set here. + */ + public DhcpServingParamsParcelExt setExcludedAddrs(@NonNull Inet4Address... excludedAddrs) { + return setExcludedAddrs(Sets.newArraySet(excludedAddrs)); + } + + /** + * Set the lease time for leases assigned by the DHCP server. + * + * <p>This parameter is required. + */ + public DhcpServingParamsParcelExt setDhcpLeaseTimeSecs(long dhcpLeaseTimeSecs) { + this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs; + return this; + } + + /** + * Set the link MTU to be advertised to DHCP clients. + * + * <p>If set to {@link #MTU_UNSET}, no MTU will be advertised to clients. This parameter + * is optional and defaults to {@link #MTU_UNSET}. + */ + public DhcpServingParamsParcelExt setLinkMtu(int linkMtu) { + this.linkMtu = linkMtu; + return this; + } + + /** + * Set whether the DHCP server should send the ANDROID_METERED vendor-specific option. + * + * <p>If not set, the default value is false. + */ + public DhcpServingParamsParcelExt setMetered(boolean metered) { + this.metered = metered; + return this; + } + + private static int[] toIntArray(@NonNull Collection<Inet4Address> addrs) { + int[] res = new int[addrs.size()]; + int i = 0; + for (Inet4Address addr : addrs) { + res[i] = inet4AddressToIntHTH(addr); + i++; + } + return res; + } +} diff --git a/services/net/java/android/net/util/SharedLog.java b/services/net/java/android/net/util/SharedLog.java index 5a73a4e492ee..74bc1470293f 100644 --- a/services/net/java/android/net/util/SharedLog.java +++ b/services/net/java/android/net/util/SharedLog.java @@ -35,8 +35,8 @@ import java.util.StringJoiner; * @hide */ public class SharedLog { - private final static int DEFAULT_MAX_RECORDS = 500; - private final static String COMPONENT_DELIMITER = "."; + private static final int DEFAULT_MAX_RECORDS = 500; + private static final String COMPONENT_DELIMITER = "."; private enum Category { NONE, @@ -69,6 +69,9 @@ public class SharedLog { mComponent = component; } + /** + * Create a SharedLog based on this log with an additional component prefix on each logged line. + */ public SharedLog forSubComponent(String component) { if (!isRootLogInstance()) { component = mComponent + COMPONENT_DELIMITER + component; @@ -76,6 +79,11 @@ public class SharedLog { return new SharedLog(mLocalLog, mTag, component); } + /** + * Dump the contents of this log. + * + * <p>This method may be called on any thread. + */ public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { mLocalLog.readOnlyLocalLog().dump(fd, writer, args); } @@ -84,10 +92,21 @@ public class SharedLog { // Methods that both log an entry and emit it to the system log. ////// + /** + * Log an error due to an exception. This does not include the exception stacktrace. + * + * <p>The log entry will be also added to the system log. + * @see #e(String, Throwable) + */ public void e(Exception e) { Log.e(mTag, record(Category.ERROR, e.toString())); } + /** + * Log an error message. + * + * <p>The log entry will be also added to the system log. + */ public void e(String msg) { Log.e(mTag, record(Category.ERROR, msg)); } @@ -96,7 +115,7 @@ public class SharedLog { * Log an error due to an exception, with the exception stacktrace if provided. * * <p>The error and exception message appear in the shared log, but the stacktrace is only - * logged in general log output (logcat). + * logged in general log output (logcat). The log entry will be also added to the system log. */ public void e(@NonNull String msg, @Nullable Throwable exception) { if (exception == null) { @@ -106,10 +125,20 @@ public class SharedLog { Log.e(mTag, record(Category.ERROR, msg + ": " + exception.getMessage()), exception); } + /** + * Log an informational message. + * + * <p>The log entry will be also added to the system log. + */ public void i(String msg) { Log.i(mTag, record(Category.NONE, msg)); } + /** + * Log a warning message. + * + * <p>The log entry will be also added to the system log. + */ public void w(String msg) { Log.w(mTag, record(Category.WARN, msg)); } @@ -118,14 +147,30 @@ public class SharedLog { // Methods that only log an entry (and do NOT emit to the system log). ////// + /** + * Log a general message to be only included in the in-memory log. + * + * <p>The log entry will *not* be added to the system log. + */ public void log(String msg) { record(Category.NONE, msg); } + /** + * Log a general, formatted message to be only included in the in-memory log. + * + * <p>The log entry will *not* be added to the system log. + * @see String#format(String, Object...) + */ public void logf(String fmt, Object... args) { log(String.format(fmt, args)); } + /** + * Log a message with MARK level. + * + * <p>The log entry will *not* be added to the system log. + */ public void mark(String msg) { record(Category.MARK, msg); } diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java index b682def9c224..769a9d4a2bd4 100644 --- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java +++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java @@ -16,6 +16,7 @@ package com.android.server.backup; +import static android.Manifest.permission.BACKUP; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startBackupThread; @@ -27,7 +28,6 @@ import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.robolectric.Shadows.shadowOf; import static org.testng.Assert.expectThrows; @@ -201,7 +201,7 @@ public class BackupManagerServiceTest { backupManagerService.stopServiceForUser(mUserOneId); verify(mUserOneService).tearDownService(); - verifyNoMoreInteractions(mUserTwoService); + verify(mUserTwoService, never()).tearDownService(); } /** Test that the service unregisters users when stopped. */ @@ -1542,6 +1542,7 @@ public class BackupManagerServiceTest { } private BackupManagerService createService() { + mShadowContext.grantPermissions(BACKUP); return new BackupManagerService( mContext, new Trampoline(mContext), startBackupThread(null)); } diff --git a/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java b/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java index b56b5e6132d5..b754356ca0cb 100644 --- a/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java +++ b/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2019 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. diff --git a/services/startop/Android.bp b/services/startop/Android.bp new file mode 100644 index 000000000000..093b4ec66ddf --- /dev/null +++ b/services/startop/Android.bp @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2018 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. + */ + +java_library_static { + name: "services.startop", + + static_libs: [ + // frameworks/base/startop/iorap + "services.startop.iorap", + ], +} diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java index abf90402250c..4742a73b17a8 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -389,7 +389,7 @@ public class DisplayManagerServiceTest { if (attr == null) return; //sampling not supported on device, skip remainder of test. boolean enabled = displayManager.setDisplayedContentSamplingEnabledInternal(0, true, 0, 0); - assertTrue(!enabled); + assertTrue(enabled); displayManager.setDisplayedContentSamplingEnabledInternal(0, false, 0, 0); DisplayedContentSample sample = displayManager.getDisplayedContentSampleInternal(0, 0, 0); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java index 7484edd286f0..4255e37aac2d 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java @@ -18,8 +18,12 @@ package com.android.server.hdmi; import android.hardware.hdmi.HdmiPortInfo; import android.hardware.tv.cec.V1_0.SendMessageResult; import android.os.MessageQueue; + import com.android.internal.annotations.VisibleForTesting; import com.android.server.hdmi.HdmiCecController.NativeWrapper; + +import com.google.common.collect.Iterables; + import java.util.ArrayList; import java.util.List; @@ -45,7 +49,6 @@ final class FakeNativeWrapper implements NativeWrapper { }; private final List<HdmiCecMessage> mResultMessages = new ArrayList<>(); - private HdmiCecMessage mResultMessage; private int mMyPhysicalAddress = 0; @Override @@ -112,11 +115,12 @@ final class FakeNativeWrapper implements NativeWrapper { return new ArrayList<>(mResultMessages); } - public HdmiCecMessage getOnlyResultMessage() throws Exception { - if (mResultMessages.size() != 1) { - throw new Exception("There is not exactly one message"); - } - return mResultMessages.get(0); + public HdmiCecMessage getOnlyResultMessage() throws IllegalArgumentException { + return Iterables.getOnlyElement(mResultMessages); + } + + public void clearResultMessages() { + mResultMessages.clear(); } public void setPollAddressResponse(int logicalAddress, int response) { diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java index 9e3a0eaa68f6..5286104633e1 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java @@ -20,18 +20,17 @@ import static com.android.server.hdmi.Constants.ADDR_BROADCAST; import static com.android.server.hdmi.Constants.ADDR_TV; import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF; -import static com.google.common.truth.Truth.assertThat; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; +import static com.google.common.truth.Truth.assertThat; import android.media.AudioManager; import android.os.Looper; import android.os.SystemProperties; import android.os.test.TestLooper; + import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; + import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource; import org.junit.Before; @@ -115,7 +114,7 @@ public class HdmiCecLocalDeviceAudioSystemTest { @Override public void setWiredDeviceConnectionState( - int type, int state, String address, String name) { + int type, int state, String address, String name) { // Do nothing. } }; @@ -123,6 +122,11 @@ public class HdmiCecLocalDeviceAudioSystemTest { @Override void wakeUp() {} + + @Override + boolean isControlEnabled() { + return true; + } }; mMyLooper = mTestLooper.getLooper(); @@ -140,11 +144,12 @@ public class HdmiCecLocalDeviceAudioSystemTest { // No TV device interacts with AVR so system audio control won't be turned on here mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); mTestLooper.dispatchAll(); + mNativeWrapper.clearResultMessages(); SystemProperties.set(Constants.PROPERTY_ARC_SUPPORT, "true"); } @Test - public void handleGiveAudioStatus_volume_10_mute_true() { + public void handleGiveAudioStatus_volume_10_mute_true() throws Exception { mMusicVolume = 10; mMusicMute = true; mMusicMaxVolume = 20; @@ -154,14 +159,13 @@ public class HdmiCecLocalDeviceAudioSystemTest { ADDR_AUDIO_SYSTEM, ADDR_TV, scaledVolume, true); HdmiCecMessage messageGive = HdmiCecMessageBuilder.buildGiveAudioStatus(ADDR_TV, ADDR_AUDIO_SYSTEM); - assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveAudioStatus(messageGive)) - .isTrue(); + assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveAudioStatus(messageGive)).isTrue(); mTestLooper.dispatchAll(); - assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage); + assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage); } @Test - public void handleGiveSystemAudioModeStatus_originalOff() { + public void handleGiveSystemAudioModeStatus_originalOff() throws Exception { HdmiCecMessage expectedMessage = HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false); HdmiCecMessage messageGive = @@ -169,7 +173,7 @@ public class HdmiCecLocalDeviceAudioSystemTest { assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive)) .isTrue(); mTestLooper.dispatchAll(); - assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage); + assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage); } @Ignore("b/80297700") @@ -228,7 +232,7 @@ public class HdmiCecLocalDeviceAudioSystemTest { } @Test - public void handleSetSystemAudioMode_setOn_orignalOff() { + public void handleSetSystemAudioMode_setOn_orignalOff() throws Exception { mMusicMute = true; HdmiCecMessage messageSet = HdmiCecMessageBuilder.buildSetSystemAudioMode(ADDR_TV, ADDR_AUDIO_SYSTEM, true); @@ -240,23 +244,23 @@ public class HdmiCecLocalDeviceAudioSystemTest { assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive)) .isTrue(); mTestLooper.dispatchAll(); - assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage); + assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage); // Check if correctly turned on + mNativeWrapper.clearResultMessages(); expectedMessage = HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, true); - assertThat(mHdmiCecLocalDeviceAudioSystem.handleSetSystemAudioMode(messageSet)) - .isTrue(); + assertThat(mHdmiCecLocalDeviceAudioSystem.handleSetSystemAudioMode(messageSet)).isTrue(); mTestLooper.dispatchAll(); assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive)) .isTrue(); mTestLooper.dispatchAll(); - assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage); + assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage); assertThat(mMusicMute).isFalse(); } @Ignore("b/80297700") @Test - public void handleSystemAudioModeRequest_turnOffByTv() { + public void handleSystemAudioModeRequest_turnOffByTv() throws Exception { assertThat(mMusicMute).isFalse(); // Check if feature correctly turned off HdmiCecMessage messageGive = @@ -270,19 +274,21 @@ public class HdmiCecLocalDeviceAudioSystemTest { assertThat(mHdmiCecLocalDeviceAudioSystem.handleSystemAudioModeRequest(messageRequestOff)) .isTrue(); mTestLooper.dispatchAll(); - assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage); + assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage); + + mNativeWrapper.clearResultMessages(); expectedMessage = HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false); assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive)) .isTrue(); mTestLooper.dispatchAll(); - assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage); + assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage); assertThat(mMusicMute).isTrue(); } @Ignore("b/80297700") @Test - public void onStandbyAudioSystem_currentSystemAudioControlOn() { + public void onStandbyAudioSystem_currentSystemAudioControlOn() throws Exception { // Set system audio control on first mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true); // Check if standby correctly turns off the feature @@ -291,12 +297,12 @@ public class HdmiCecLocalDeviceAudioSystemTest { HdmiCecMessage expectedMessage = HdmiCecMessageBuilder.buildSetSystemAudioMode( ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false); - assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage); + assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage); assertThat(mMusicMute).isTrue(); } @Test - public void systemAudioControlOnPowerOn_alwaysOn() { + public void systemAudioControlOnPowerOn_alwaysOn() throws Exception { mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class); mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn( Constants.ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true); @@ -307,7 +313,7 @@ public class HdmiCecLocalDeviceAudioSystemTest { } @Test - public void systemAudioControlOnPowerOn_neverOn() { + public void systemAudioControlOnPowerOn_neverOn() throws Exception { mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class); mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn( Constants.NEVER_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false); @@ -318,7 +324,7 @@ public class HdmiCecLocalDeviceAudioSystemTest { } @Test - public void systemAudioControlOnPowerOn_useLastState_off() { + public void systemAudioControlOnPowerOn_useLastState_off() throws Exception { mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class); mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn( Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false); @@ -329,7 +335,7 @@ public class HdmiCecLocalDeviceAudioSystemTest { } @Test - public void systemAudioControlOnPowerOn_useLastState_on() { + public void systemAudioControlOnPowerOn_useLastState_on() throws Exception { mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class); mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn( Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true); @@ -340,18 +346,17 @@ public class HdmiCecLocalDeviceAudioSystemTest { } @Test - public void handleActiveSource_updateActiveSource() { + public void handleActiveSource_updateActiveSource() throws Exception { HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); ActiveSource expectedActiveSource = new ActiveSource(ADDR_TV, 0x0000); - assertThat(mHdmiCecLocalDeviceAudioSystem.handleActiveSource(message)) - .isTrue(); + assertThat(mHdmiCecLocalDeviceAudioSystem.handleActiveSource(message)).isTrue(); mTestLooper.dispatchAll(); assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource().equals(expectedActiveSource)) .isTrue(); } @Test - public void terminateSystemAudioMode_systemAudioModeOff() { + public void terminateSystemAudioMode_systemAudioModeOff() throws Exception { mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(false); assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isFalse(); mMusicMute = false; @@ -361,12 +366,12 @@ public class HdmiCecLocalDeviceAudioSystemTest { mHdmiCecLocalDeviceAudioSystem.terminateSystemAudioMode(); assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isFalse(); assertThat(mMusicMute).isFalse(); - assertThat(mNativeWrapper.getResultMessages()).doesNotContain(message); + assertThat(mNativeWrapper.getResultMessages()).isEmpty(); } @Ignore("b/80297700") @Test - public void terminateSystemAudioMode_systemAudioModeOn() { + public void terminateSystemAudioMode_systemAudioModeOn() throws Exception { mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true); assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isTrue(); mMusicMute = false; @@ -381,124 +386,131 @@ public class HdmiCecLocalDeviceAudioSystemTest { } @Test - public void isPhysicalAddressMeOrBelow_isMe() { + public void isPhysicalAddressMeOrBelow_isMe() throws Exception { int targetPhysicalAddress = 0x1000; mNativeWrapper.setPhysicalAddress(0x1000); assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress)) - .isTrue(); + .isTrue(); } @Test - public void isPhysicalAddressMeOrBelow_isBelow() { + public void isPhysicalAddressMeOrBelow_isBelow() throws Exception { int targetPhysicalAddress = 0x1100; mNativeWrapper.setPhysicalAddress(0x1000); assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress)) - .isTrue(); + .isTrue(); } @Test - public void isPhysicalAddressMeOrBelow_neitherMeNorBelow() { + public void isPhysicalAddressMeOrBelow_neitherMeNorBelow() throws Exception { int targetPhysicalAddress = 0x3000; mNativeWrapper.setPhysicalAddress(0x2000); assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress)) - .isFalse(); + .isFalse(); targetPhysicalAddress = 0x2200; mNativeWrapper.setPhysicalAddress(0x3300); assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress)) - .isFalse(); + .isFalse(); targetPhysicalAddress = 0x2213; mNativeWrapper.setPhysicalAddress(0x2212); assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress)) - .isFalse(); + .isFalse(); targetPhysicalAddress = 0x2340; mNativeWrapper.setPhysicalAddress(0x2310); assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress)) - .isFalse(); + .isFalse(); } @Test - public void handleRequestArcInitiate_isNotDirectConnectedToTv() { - HdmiCecMessage message = HdmiCecMessageBuilder - .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM); - HdmiCecMessage expectedMessage = HdmiCecMessageBuilder - .buildFeatureAbortCommand( - ADDR_AUDIO_SYSTEM, ADDR_TV, - Constants.MESSAGE_REQUEST_ARC_INITIATION, - Constants.ABORT_NOT_IN_CORRECT_MODE); + public void handleRequestArcInitiate_isNotDirectConnectedToTv() throws Exception { + HdmiCecMessage message = + HdmiCecMessageBuilder.buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM); + HdmiCecMessage expectedMessage = + HdmiCecMessageBuilder.buildFeatureAbortCommand( + ADDR_AUDIO_SYSTEM, + ADDR_TV, + Constants.MESSAGE_REQUEST_ARC_INITIATION, + Constants.ABORT_NOT_IN_CORRECT_MODE); mNativeWrapper.setPhysicalAddress(0x1100); - assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message)) - .isTrue(); + assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message)).isTrue(); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage); } @Test - public void handleRequestArcInitiate_startArcInitiationActionFromAvr() { - HdmiCecMessage message = HdmiCecMessageBuilder - .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM); + public void handleRequestArcInitiate_startArcInitiationActionFromAvr() throws Exception { + HdmiCecMessage message = + HdmiCecMessageBuilder.buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM); mNativeWrapper.setPhysicalAddress(0x1000); - mHdmiCecLocalDeviceAudioSystem.removeAction( - ArcInitiationActionFromAvr.class); + mHdmiCecLocalDeviceAudioSystem.removeAction(ArcInitiationActionFromAvr.class); - assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message)) - .isTrue(); + assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message)).isTrue(); mTestLooper.dispatchAll(); - assertThat(mHdmiCecLocalDeviceAudioSystem - .getActions(ArcInitiationActionFromAvr.class)).isNotEmpty(); + assertThat(mHdmiCecLocalDeviceAudioSystem.getActions(ArcInitiationActionFromAvr.class)) + .isNotEmpty(); } @Test - public void handleRequestArcTerminate_arcIsOn_startTerminationActionFromAvr() { + public void handleRequestArcTerminate_arcIsOn_startTerminationActionFromAvr() throws Exception { mHdmiCecLocalDeviceAudioSystem.setArcStatus(true); assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isTrue(); - HdmiCecMessage message = HdmiCecMessageBuilder - .buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM); - mHdmiCecLocalDeviceAudioSystem.removeAction( - ArcTerminationActionFromAvr.class); + HdmiCecMessage message = + HdmiCecMessageBuilder.buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM); + mHdmiCecLocalDeviceAudioSystem.removeAction(ArcTerminationActionFromAvr.class); - assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(message)) - .isTrue(); + assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(message)).isTrue(); mTestLooper.dispatchAll(); - assertThat(mHdmiCecLocalDeviceAudioSystem - .getActions(ArcTerminationActionFromAvr.class)).isNotEmpty(); + assertThat(mHdmiCecLocalDeviceAudioSystem.getActions(ArcTerminationActionFromAvr.class)) + .isNotEmpty(); } @Test - public void handleRequestArcTerminate_arcIsNotOn() { + public void handleRequestArcTerminate_arcIsNotOn() throws Exception { assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse(); - HdmiCecMessage message = HdmiCecMessageBuilder - .buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM); - HdmiCecMessage expectedMessage = HdmiCecMessageBuilder - .buildFeatureAbortCommand( - ADDR_AUDIO_SYSTEM, ADDR_TV, - Constants.MESSAGE_REQUEST_ARC_TERMINATION, - Constants.ABORT_NOT_IN_CORRECT_MODE); - - assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(message)) - .isTrue(); + HdmiCecMessage message = + HdmiCecMessageBuilder.buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM); + HdmiCecMessage expectedMessage = + HdmiCecMessageBuilder.buildFeatureAbortCommand( + ADDR_AUDIO_SYSTEM, + ADDR_TV, + Constants.MESSAGE_REQUEST_ARC_TERMINATION, + Constants.ABORT_NOT_IN_CORRECT_MODE); + + assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(message)).isTrue(); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage); } @Test - public void handleRequestArcInit_arcIsNotSupported() { - HdmiCecMessage message = HdmiCecMessageBuilder - .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM); - HdmiCecMessage expectedMessage = HdmiCecMessageBuilder - .buildFeatureAbortCommand( - ADDR_AUDIO_SYSTEM, ADDR_TV, - Constants.MESSAGE_REQUEST_ARC_INITIATION, - Constants.ABORT_UNRECOGNIZED_OPCODE); + public void handleRequestArcInit_arcIsNotSupported() throws Exception { + HdmiCecMessage message = + HdmiCecMessageBuilder.buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM); + HdmiCecMessage expectedMessage = + HdmiCecMessageBuilder.buildFeatureAbortCommand( + ADDR_AUDIO_SYSTEM, + ADDR_TV, + Constants.MESSAGE_REQUEST_ARC_INITIATION, + Constants.ABORT_UNRECOGNIZED_OPCODE); SystemProperties.set(Constants.PROPERTY_ARC_SUPPORT, "false"); - assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message)) - .isTrue(); + assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message)).isTrue(); mTestLooper.dispatchAll(); - assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage); + assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage); + } + + @Test + public void onStandby_setAutoDeviceOff_true() throws Exception { + HdmiCecMessage expectedMessage = + HdmiCecMessageBuilder.buildStandby(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST); + mHdmiCecLocalDeviceAudioSystem.setAutoDeviceOff(true); + mHdmiCecLocalDeviceAudioSystem.onStandby(false, STANDBY_SCREEN_OFF); + + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage); } } diff --git a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java index 8496a961959d..b348aeef802e 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java @@ -699,10 +699,52 @@ public class AppTimeLimitControllerTests { assertTrue(hasUsageSessionObserver(UID, OBS_ID1)); } + /** Verify the timeout message is delivered at the right time after past usage was reported */ + @Test + public void testAppUsageObserver_PastUsage() throws Exception { + setTime(10_000L); + addAppUsageObserver(OBS_ID1, GROUP1, 6_000L); + setTime(20_000L); + startPastUsage(PKG_SOC1, 5_000); + setTime(21_000L); + assertTrue(mLimitReachedLatch.await(2_000L, TimeUnit.MILLISECONDS)); + stopUsage(PKG_SOC1); + // Verify that the observer was removed + assertFalse(hasAppUsageObserver(UID, OBS_ID1)); + } + + /** + * Verify the timeout message is delivered at the right time after past usage was reported + * that overlaps with already known usage + */ + @Test + public void testAppUsageObserver_PastUsageOverlap() throws Exception { + setTime(0L); + addAppUsageObserver(OBS_ID1, GROUP1, 20_000L); + setTime(10_000L); + startUsage(PKG_SOC1); + setTime(20_000L); + stopUsage(PKG_SOC1); + setTime(25_000L); + startPastUsage(PKG_SOC1, 9_000); + setTime(26_000L); + // the 4 seconds of overlapped usage should not be counted + assertFalse(mLimitReachedLatch.await(2_000L, TimeUnit.MILLISECONDS)); + setTime(30_000L); + assertTrue(mLimitReachedLatch.await(4_000L, TimeUnit.MILLISECONDS)); + stopUsage(PKG_SOC1); + // Verify that the observer was removed + assertFalse(hasAppUsageObserver(UID, OBS_ID1)); + } + private void startUsage(String packageName) { mController.noteUsageStart(packageName, USER_ID); } + private void startPastUsage(String packageName, int timeAgo) { + mController.noteUsageStart(packageName, USER_ID, timeAgo); + } + private void stopUsage(String packageName) { mController.noteUsageStop(packageName, USER_ID); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java index 8b65e763b088..20f72bfe9938 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java @@ -613,7 +613,7 @@ public class ManagedServicesTest extends UiServiceTestCase { } @Test - public void testGetAllowedPackages() throws Exception { + public void testGetAllowedPackages_byUser() throws Exception { for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) { ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm, approvalLevel); @@ -681,6 +681,30 @@ public class ManagedServicesTest extends UiServiceTestCase { } @Test + public void testGetAllowedPackages() throws Exception { + ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, + mIpm, APPROVAL_BY_COMPONENT); + loadXml(service); + service.mApprovalLevel = APPROVAL_BY_PACKAGE; + loadXml(service); + + List<String> allowedPackages = new ArrayList<>(); + allowedPackages.add("this.is.a.package.name"); + allowedPackages.add("another.package"); + allowedPackages.add("secondary"); + allowedPackages.add("this.is.another.package"); + allowedPackages.add("package"); + allowedPackages.add("component"); + allowedPackages.add("bananas!"); + + Set<String> actual = service.getAllowedPackages(); + assertEquals(allowedPackages.size(), actual.size()); + for (String pkg : allowedPackages) { + assertTrue(actual.contains(pkg)); + } + } + + @Test public void testOnUserRemoved() throws Exception { for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) { ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java index 49f134fdeac0..174c5fa6a51e 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java @@ -299,6 +299,59 @@ public class SnoozeHelperTest extends UiServiceTestCase { assertEquals(1, mSnoozeHelper.getSnoozed(UserHandle.USER_SYSTEM, "pkg").size()); } + @Test + public void testClearData() { + // snooze 2 from same package + NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); + NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM); + mSnoozeHelper.snooze(r, 1000); + mSnoozeHelper.snooze(r2, 1000); + assertTrue(mSnoozeHelper.isSnoozed( + UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey())); + assertTrue(mSnoozeHelper.isSnoozed( + UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey())); + + // clear data + mSnoozeHelper.clearData(UserHandle.USER_SYSTEM, "pkg"); + + // nothing snoozed; alarms canceled + assertFalse(mSnoozeHelper.isSnoozed( + UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey())); + assertFalse(mSnoozeHelper.isSnoozed( + UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey())); + // twice for initial snooze, twice for canceling the snooze + verify(mAm, times(4)).cancel(any(PendingIntent.class)); + } + + @Test + public void testClearData_otherRecordsUntouched() { + // 2 packages, 2 users + NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); + NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.ALL); + NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM); + mSnoozeHelper.snooze(r, 1000); + mSnoozeHelper.snooze(r2, 1000); + mSnoozeHelper.snooze(r3, 1000); + assertTrue(mSnoozeHelper.isSnoozed( + UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey())); + assertTrue(mSnoozeHelper.isSnoozed( + UserHandle.USER_ALL, r2.sbn.getPackageName(), r2.getKey())); + assertTrue(mSnoozeHelper.isSnoozed( + UserHandle.USER_SYSTEM, r3.sbn.getPackageName(), r3.getKey())); + + // clear data + mSnoozeHelper.clearData(UserHandle.USER_SYSTEM, "pkg"); + + assertFalse(mSnoozeHelper.isSnoozed( + UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey())); + assertTrue(mSnoozeHelper.isSnoozed( + UserHandle.USER_ALL, r2.sbn.getPackageName(), r2.getKey())); + assertTrue(mSnoozeHelper.isSnoozed( + UserHandle.USER_SYSTEM, r3.sbn.getPackageName(), r3.getKey())); + // once for each initial snooze, once for canceling one snooze + verify(mAm, times(4)).cancel(any(PendingIntent.class)); + } + private NotificationRecord getNotificationRecord(String pkg, int id, String tag, UserHandle user, String groupKey, boolean groupSummary) { Notification n = new Notification.Builder(getContext(), TEST_CHANNEL_ID) diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java index 569c6d4af37d..ee336a8c87f2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -31,6 +31,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; @@ -38,6 +39,9 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING; import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doCallRealMethod; + import android.app.ActivityManagerInternal; import android.app.ActivityOptions; import android.app.IApplicationThread; @@ -115,6 +119,10 @@ class ActivityTestsBase { @After public void tearDownBase() { mTestInjector.tearDown(); + if (mService != null) { + mService.setWindowManager(null); + mService = null; + } } ActivityTaskManagerService createActivityTaskManagerService() { @@ -144,6 +152,13 @@ class ActivityTestsBase { return display; } + /** Creates and adds a {@link TestActivityDisplay} to supervisor at the given position. */ + TestActivityDisplay addNewActivityDisplayAt(DisplayInfo info, int position) { + final TestActivityDisplay display = createNewActivityDisplay(info); + mRootActivityContainer.addChild(display, position); + return display; + } + /** * Builder for creating new activities. */ @@ -234,6 +249,10 @@ class ActivityTestsBase { mService.mStackSupervisor, null /* options */, null /* sourceRecord */); spyOn(activity); activity.mAppWindowToken = mock(AppWindowToken.class); + doCallRealMethod().when(activity.mAppWindowToken).getOrientationIgnoreVisibility(); + doCallRealMethod().when(activity.mAppWindowToken) + .setOrientation(anyInt(), any(), any()); + doCallRealMethod().when(activity.mAppWindowToken).setOrientation(anyInt()); doNothing().when(activity).removeWindowContainer(); if (mTaskRecord != null) { @@ -346,6 +365,7 @@ class ActivityTestsBase { mStack.addTask(task, true, "creating test task"); task.setStack(mStack); task.setTask(); + mStack.getWindowContainerController().mContainer.addChild(task.mTask, 0); } task.touchActiveTime(); @@ -365,7 +385,10 @@ class ActivityTestsBase { setTask(); } - private void setTask() { + void setTask() { + Task mockTask = mock(Task.class); + mockTask.mTaskRecord = this; + doCallRealMethod().when(mockTask).onDescendantOrientationChanged(any(), any()); setTask(mock(Task.class)); } } @@ -619,7 +642,13 @@ class ActivityTestsBase { } } + private static WindowManagerService sMockWindowManagerService; + private static WindowManagerService prepareMockWindowManager() { + if (sMockWindowManagerService != null) { + return sMockWindowManagerService; + } + final WindowManagerService service = mock(WindowManagerService.class); service.mRoot = mock(RootWindowContainer.class); @@ -631,6 +660,7 @@ class ActivityTestsBase { return null; }).when(service).inSurfaceTransaction(any()); + sMockWindowManagerService = service; return service; } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java index 6b31e6fdbd28..198e7ce63f52 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java @@ -63,6 +63,7 @@ import com.android.server.statusbar.StatusBarManagerInternal; import org.junit.After; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -86,7 +87,7 @@ public class DisplayRotationTests { private StatusBarManagerInternal mPreviousStatusBarManagerInternal; - private WindowManagerService mMockWm; + private static WindowManagerService sMockWm; private DisplayContent mMockDisplayContent; private DisplayPolicy mMockDisplayPolicy; private Context mMockContext; @@ -108,13 +109,16 @@ public class DisplayRotationTests { private DisplayRotation mTarget; + @BeforeClass + public static void setUpOnce() { + sMockWm = mock(WindowManagerService.class); + sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class); + } + @Before public void setUp() { FakeSettingsProvider.clearSettingsProvider(); - mMockWm = mock(WindowManagerService.class); - mMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class); - mPreviousStatusBarManagerInternal = LocalServices.getService( StatusBarManagerInternal.class); LocalServices.removeServiceForTest(StatusBarManagerInternal.class); @@ -452,7 +456,7 @@ public class DisplayRotationTests { mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90)); assertTrue(waitForUiHandler()); - verify(mMockWm).updateRotation(false, false); + verify(sMockWm).updateRotation(false, false); } @Test @@ -833,8 +837,9 @@ public class DisplayRotationTests { .thenReturn(mFakeSettingsProvider.getIContentProvider()); mMockDisplayWindowSettings = mock(DisplayWindowSettings.class); - mTarget = new DisplayRotation(mMockWm, mMockDisplayContent, mMockDisplayPolicy, + mTarget = new DisplayRotation(sMockWm, mMockDisplayContent, mMockDisplayPolicy, mMockDisplayWindowSettings, mMockContext, new Object()); + reset(sMockWm); captureObservers(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java index a9f150b10e73..e24eb75a2750 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java @@ -34,7 +34,12 @@ import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.isNull; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; @@ -47,10 +52,6 @@ import static com.android.server.wm.LockTaskController.STATUS_BAR_MASK_PINNED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; import android.app.StatusBarManager; import android.app.admin.DevicePolicyManager; @@ -115,6 +116,7 @@ public class LockTaskControllerTest { private LockTaskController mLockTaskController; private Context mContext; + private String mPackageName; private String mLockToAppSetting; @Before @@ -122,6 +124,7 @@ public class LockTaskControllerTest { MockitoAnnotations.initMocks(this); mContext = getInstrumentation().getTargetContext(); + mPackageName = mContext.getPackageName(); mLockToAppSetting = Settings.Secure.getString(mContext.getContentResolver(), Settings.Secure.LOCK_TO_APP_EXIT_LOCKED); @@ -146,6 +149,7 @@ public class LockTaskControllerTest { @After public void tearDown() throws Exception { + mLockTaskController.setWindowManager(null); Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, mLockToAppSetting); } @@ -223,7 +227,7 @@ public class LockTaskControllerTest { // THEN lock task mode should be started verifyLockTaskStarted(STATUS_BAR_MASK_PINNED, DISABLE2_NONE); // THEN screen pinning toast should be shown - verify(mStatusBarService).showPinningEnterExitToast(true /* entering */); + verify(mStatusBarService).showPinningEnterExitToast(eq(true /* entering */)); } @Test @@ -390,9 +394,9 @@ public class LockTaskControllerTest { // THEN lock task mode should have been finished verifyLockTaskStopped(times(1)); // THEN the keyguard should be shown - verify(mLockPatternUtils).requireCredentialEntry(UserHandle.USER_ALL); + verify(mLockPatternUtils).requireCredentialEntry(eq(UserHandle.USER_ALL)); // THEN screen pinning toast should be shown - verify(mStatusBarService).showPinningEnterExitToast(false /* entering */); + verify(mStatusBarService).showPinningEnterExitToast(eq(false /* entering */)); } @Test @@ -509,9 +513,9 @@ public class LockTaskControllerTest { & ~DISABLE_HOME; int expectedFlags2 = DISABLE2_MASK; verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class), - eq(mContext.getPackageName())); + eq(mPackageName)); verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class), - eq(mContext.getPackageName())); + eq(mPackageName)); // reset invocation counter reset(mStatusBarService); @@ -526,9 +530,9 @@ public class LockTaskControllerTest { expectedFlags2 = DISABLE2_MASK & ~DISABLE2_NOTIFICATION_SHADE; verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class), - eq(mContext.getPackageName())); + eq(mPackageName)); verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class), - eq(mContext.getPackageName())); + eq(mPackageName)); } @Test @@ -548,9 +552,9 @@ public class LockTaskControllerTest { // THEN status bar shouldn't change verify(mStatusBarService, never()).disable(anyInt(), any(IBinder.class), - eq(mContext.getPackageName())); + eq(mPackageName)); verify(mStatusBarService, never()).disable2(anyInt(), any(IBinder.class), - eq(mContext.getPackageName())); + eq(mPackageName)); } @Test @@ -657,14 +661,14 @@ public class LockTaskControllerTest { verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString(), eq(TEST_USER_ID)); // THEN the status bar should have been disabled verify(mStatusBarService).disable(eq(statusBarMask), any(IBinder.class), - eq(mContext.getPackageName())); + eq(mPackageName)); verify(mStatusBarService).disable2(eq(statusBarMask2), any(IBinder.class), - eq(mContext.getPackageName())); + eq(mPackageName)); // THEN recents should have been notified verify(mRecentTasks).onLockTaskModeStateChanged(anyInt(), eq(TEST_USER_ID)); // THEN the DO/PO should be informed about the operation - verify(mDevicePolicyManager).notifyLockTaskModeChanged(true, TEST_PACKAGE_NAME, - TEST_USER_ID); + verify(mDevicePolicyManager).notifyLockTaskModeChanged(eq(true), eq(TEST_PACKAGE_NAME), + eq(TEST_USER_ID)); } private void verifyLockTaskStopped(VerificationMode mode) throws Exception { @@ -672,11 +676,12 @@ public class LockTaskControllerTest { verify(mWindowManager, mode).reenableKeyguard(any(IBinder.class), eq(TEST_USER_ID)); // THEN the status bar should have been disabled verify(mStatusBarService, mode).disable(eq(StatusBarManager.DISABLE_NONE), - any(IBinder.class), eq(mContext.getPackageName())); + any(IBinder.class), eq(mPackageName)); verify(mStatusBarService, mode).disable2(eq(StatusBarManager.DISABLE2_NONE), - any(IBinder.class), eq(mContext.getPackageName())); + any(IBinder.class), eq(mPackageName)); // THEN the DO/PO should be informed about the operation - verify(mDevicePolicyManager, mode).notifyLockTaskModeChanged(false, null, TEST_USER_ID); + verify(mDevicePolicyManager, mode).notifyLockTaskModeChanged(eq(false), isNull(), + eq(TEST_USER_ID)); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java index ad2a708b88d9..413b6f4b3905 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java @@ -143,8 +143,8 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { @Test public void testTimeout_scaled() throws Exception { - mWm.setAnimationScale(2, 5.0f); try { + mWm.setAnimationScale(2, 5.0f); final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken, diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java index 36eccd1892a7..03aba39517eb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java @@ -33,6 +33,8 @@ import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; @@ -58,7 +60,6 @@ import android.view.WindowInsets; import android.view.WindowManager; import android.widget.TextView; -import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.After; @@ -80,8 +81,8 @@ import java.util.function.BooleanSupplier; @Presubmit public class ScreenDecorWindowTests { - private final Context mContext = InstrumentationRegistry.getTargetContext(); - private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation(); + private final Context mContext = getInstrumentation().getTargetContext(); + private final Instrumentation mInstrumentation = getInstrumentation(); private WindowManager mWm; private ArrayList<View> mWindows = new ArrayList<>(); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index 7da85af444d6..00bec3fa4936 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -21,6 +21,11 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + +import static com.android.server.wm.WindowContainer.POSITION_TOP; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.sameInstance; @@ -133,17 +138,6 @@ public class TaskRecordTests extends ActivityTestsBase { assertTrue(task.returnsToHomeStack()); } - /** Ensures that bounds are clipped to their parent. */ - @Test - public void testAppBounds_BoundsClipping() { - final Rect shiftedBounds = new Rect(mParentBounds); - shiftedBounds.offset(10, 10); - final Rect expectedBounds = new Rect(mParentBounds); - expectedBounds.intersect(shiftedBounds); - testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, shiftedBounds, - expectedBounds); - } - /** Ensures that empty bounds are not propagated to the configuration. */ @Test public void testAppBounds_EmptyBounds() { @@ -167,18 +161,108 @@ public class TaskRecordTests extends ActivityTestsBase { final Rect insetBounds = new Rect(mParentBounds); insetBounds.inset(5, 5, 5, 5); testStackBoundsConfiguration( - WINDOWING_MODE_FULLSCREEN, mParentBounds, insetBounds, insetBounds); + WINDOWING_MODE_FREEFORM, mParentBounds, insetBounds, insetBounds); } - /** Ensures that full screen free form bounds are clipped */ + /** Tests that the task bounds adjust properly to changes between FULLSCREEN and FREEFORM */ @Test - public void testAppBounds_FullScreenFreeFormBounds() { + public void testBoundsOnModeChangeFreeformToFullscreen() { ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay(); + ActivityStack stack = new StackBuilder(mRootActivityContainer).setDisplay(display) + .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); + TaskRecord task = stack.getChildAt(0); + task.getRootActivity().mAppWindowToken.setOrientation(SCREEN_ORIENTATION_UNSPECIFIED); DisplayInfo info = new DisplayInfo(); display.mDisplay.getDisplayInfo(info); final Rect fullScreenBounds = new Rect(0, 0, info.logicalWidth, info.logicalHeight); - testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, fullScreenBounds, - mParentBounds); + final Rect freeformBounds = new Rect(fullScreenBounds); + freeformBounds.inset((int) (freeformBounds.width() * 0.2), + (int) (freeformBounds.height() * 0.2)); + task.setBounds(freeformBounds); + + assertEquals(freeformBounds, task.getBounds()); + + // FULLSCREEN inherits bounds + stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + assertEquals(fullScreenBounds, task.getBounds()); + assertEquals(freeformBounds, task.mLastNonFullscreenBounds); + + // FREEFORM restores bounds + stack.setWindowingMode(WINDOWING_MODE_FREEFORM); + assertEquals(freeformBounds, task.getBounds()); + } + + /** + * This is a temporary hack to trigger an onConfigurationChange at the task level after an + * orientation is requested. Normally this is done by the onDescendentOrientationChanged call + * up the WM hierarchy, but since the WM hierarchy is mocked out, it doesn't happen here. + * TODO: remove this when we either get a WM hierarchy or when hierarchies are merged. + */ + private void setActivityRequestedOrientation(ActivityRecord activity, int orientation) { + activity.setRequestedOrientation(orientation); + ConfigurationContainer taskRecord = activity.getParent(); + taskRecord.onConfigurationChanged(taskRecord.getParent().getConfiguration()); + } + + /** + * Tests that a task with forced orientation has orientation-consistent bounds within the + * parent. + */ + @Test + public void testFullscreenBoundsForcedOrientation() { + final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080); + final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920); + DisplayInfo info = new DisplayInfo(); + info.logicalWidth = fullScreenBounds.width(); + info.logicalHeight = fullScreenBounds.height(); + ActivityDisplay display = addNewActivityDisplayAt(info, POSITION_TOP); + assertTrue(mRootActivityContainer.getActivityDisplay(display.mDisplayId) != null); + ActivityStack stack = new StackBuilder(mRootActivityContainer) + .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); + TaskRecord task = stack.getChildAt(0); + ActivityRecord root = task.getRootActivity(); + ActivityRecord top = new ActivityBuilder(mService).setTask(task).setStack(stack).build(); + assertEquals(root, task.getRootActivity()); + + assertEquals(fullScreenBounds, task.getBounds()); + + // Setting app to fixed portrait fits within parent + setActivityRequestedOrientation(root, SCREEN_ORIENTATION_PORTRAIT); + assertEquals(root, task.getRootActivity()); + assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation()); + assertTrue(task.getBounds().width() < task.getBounds().height()); + assertEquals(fullScreenBounds.height(), task.getBounds().height()); + + // Setting non-root app has no effect + setActivityRequestedOrientation(root, SCREEN_ORIENTATION_LANDSCAPE); + assertTrue(task.getBounds().width() < task.getBounds().height()); + + // Setting app to unspecified restores + setActivityRequestedOrientation(root, SCREEN_ORIENTATION_UNSPECIFIED); + assertEquals(fullScreenBounds, task.getBounds()); + + // Setting app to fixed landscape and changing display + setActivityRequestedOrientation(root, SCREEN_ORIENTATION_LANDSCAPE); + display.setBounds(fullScreenBoundsPort); + assertTrue(task.getBounds().width() > task.getBounds().height()); + assertEquals(fullScreenBoundsPort.width(), task.getBounds().width()); + + // in FREEFORM, no constraint + final Rect freeformBounds = new Rect(display.getBounds()); + freeformBounds.inset((int) (freeformBounds.width() * 0.2), + (int) (freeformBounds.height() * 0.2)); + stack.setWindowingMode(WINDOWING_MODE_FREEFORM); + task.setBounds(freeformBounds); + assertEquals(freeformBounds, task.getBounds()); + + // FULLSCREEN letterboxes bounds + stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + assertTrue(task.getBounds().width() > task.getBounds().height()); + assertEquals(fullScreenBoundsPort.width(), task.getBounds().width()); + + // FREEFORM restores bounds as before + stack.setWindowingMode(WINDOWING_MODE_FREEFORM); + assertEquals(freeformBounds, task.getBounds()); } /** Ensures that the alias intent won't have target component resolved. */ diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java index 624ef9ba1653..ca815eca9a1f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java @@ -39,6 +39,7 @@ import android.graphics.PixelFormat; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.view.Surface; +import android.view.SurfaceControl; import androidx.test.filters.SmallTest; @@ -65,7 +66,7 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase { final TaskSnapshot snapshot = new TaskSnapshot(new ComponentName("", ""), buffer, ORIENTATION_PORTRAIT, contentInsets, false, 1.0f, true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN, 0 /* systemUiVisibility */, false /* isTranslucent */); - mSurface = new TaskSnapshotSurface(mWm, new Window(), new Surface(), snapshot, "Test", + mSurface = new TaskSnapshotSurface(mWm, new Window(), new SurfaceControl(), snapshot, "Test", Color.WHITE, Color.RED, Color.BLUE, sysuiVis, windowFlags, 0, taskBounds, ORIENTATION_PORTRAIT); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java deleted file mode 100644 index 04e433e98678..000000000000 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader; -import static android.view.Display.DEFAULT_DISPLAY; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.when; - -import android.app.ActivityManagerInternal; -import android.content.Context; -import android.hardware.display.DisplayManagerInternal; -import android.os.PowerManagerInternal; -import android.os.PowerSaveState; -import android.view.Display; -import android.view.InputChannel; -import android.view.SurfaceControl; -import android.view.SurfaceControl.Transaction; - -import com.android.server.LocalServices; -import com.android.server.input.InputManagerService; -import com.android.server.policy.WindowManagerPolicy; - -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import org.mockito.invocation.InvocationOnMock; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; - -/** - * A test rule that sets up a fresh WindowManagerService instance before each test and makes sure - * to properly tear it down after. - * - * <p> - * Usage: - * <pre> - * {@literal @}Rule - * public final WindowManagerServiceRule mWmRule = new WindowManagerServiceRule(); - * </pre> - */ -public class WindowManagerServiceRule implements TestRule { - - private WindowManagerService mService; - private TestWindowManagerPolicy mPolicy; - // Record all {@link SurfaceControl.Transaction} created while testing and releases native - // resources when test finishes. - private final List<WeakReference<Transaction>> mSurfaceTransactions = new ArrayList<>(); - // Record all {@link SurfaceControl} created while testing and releases native resources when - // test finishes. - private final List<WeakReference<SurfaceControl>> mSurfaceControls = new ArrayList<>(); - - @Override - public Statement apply(Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - runWithDexmakerShareClassLoader(this::setUp); - try { - base.evaluate(); - } finally { - tearDown(); - } - } - - private void setUp() { - final Context context = getInstrumentation().getTargetContext(); - - removeServices(); - - LocalServices.addService(DisplayManagerInternal.class, - mock(DisplayManagerInternal.class)); - - LocalServices.addService(PowerManagerInternal.class, - mock(PowerManagerInternal.class)); - final PowerManagerInternal pm = - LocalServices.getService(PowerManagerInternal.class); - doNothing().when(pm).registerLowPowerModeObserver(any()); - PowerSaveState state = new PowerSaveState.Builder().build(); - doReturn(state).when(pm).getLowPowerState(anyInt()); - - LocalServices.addService(ActivityManagerInternal.class, - mock(ActivityManagerInternal.class)); - LocalServices.addService(ActivityTaskManagerInternal.class, - mock(ActivityTaskManagerInternal.class)); - final ActivityTaskManagerInternal atm = - LocalServices.getService(ActivityTaskManagerInternal.class); - doAnswer((InvocationOnMock invocationOnMock) -> { - final Runnable runnable = invocationOnMock.<Runnable>getArgument(0); - if (runnable != null) { - runnable.run(); - } - return null; - }).when(atm).notifyKeyguardFlagsChanged(any(), anyInt()); - - InputManagerService ims = mock(InputManagerService.class); - // InputChannel is final and can't be mocked. - InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM); - if (input != null && input.length > 1) { - doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt()); - } - ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class); - when(atms.getGlobalLock()).thenReturn(new WindowManagerGlobalLock()); - - mService = WindowManagerService.main(context, ims, false, false, - mPolicy = new TestWindowManagerPolicy( - WindowManagerServiceRule.this::getWindowManagerService), atms); - mService.mTransactionFactory = () -> { - final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction(); - mSurfaceTransactions.add(new WeakReference<>(transaction)); - return transaction; - }; - mService.mSurfaceBuilderFactory = session -> new SurfaceControl.Builder(session) { - @Override - public SurfaceControl build() { - final SurfaceControl control = super.build(); - mSurfaceControls.add(new WeakReference<>(control)); - return control; - } - }; - - mService.onInitReady(); - - final Display display = mService.mDisplayManager.getDisplay(DEFAULT_DISPLAY); - // Display creation is driven by the ActivityManagerService via - // ActivityStackSupervisor. We emulate those steps here. - mService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class)); - } - - private void removeServices() { - LocalServices.removeServiceForTest(DisplayManagerInternal.class); - LocalServices.removeServiceForTest(PowerManagerInternal.class); - LocalServices.removeServiceForTest(ActivityManagerInternal.class); - LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class); - LocalServices.removeServiceForTest(WindowManagerInternal.class); - LocalServices.removeServiceForTest(WindowManagerPolicy.class); - } - - private void tearDown() { - cancelAllPendingAnimations(); - waitUntilWindowManagerHandlersIdle(); - destroyAllSurfaceTransactions(); - destroyAllSurfaceControls(); - removeServices(); - mService = null; - mPolicy = null; - } - }; - } - - WindowManagerService getWindowManagerService() { - return mService; - } - - private void cancelAllPendingAnimations() { - for (final WeakReference<SurfaceControl> reference : mSurfaceControls) { - final SurfaceControl sc = reference.get(); - if (sc != null) { - mService.mSurfaceAnimationRunner.onAnimationCancelled(sc); - } - } - } - - void waitUntilWindowManagerHandlersIdle() { - final WindowManagerService wm = getWindowManagerService(); - if (wm == null) { - return; - } - wm.mH.removeCallbacksAndMessages(null); - wm.mAnimationHandler.removeCallbacksAndMessages(null); - SurfaceAnimationThread.getHandler().removeCallbacksAndMessages(null); - wm.mH.runWithScissors(() -> { }, 0); - wm.mAnimationHandler.runWithScissors(() -> { }, 0); - SurfaceAnimationThread.getHandler().runWithScissors(() -> { }, 0); - } - - private void destroyAllSurfaceTransactions() { - for (final WeakReference<Transaction> reference : mSurfaceTransactions) { - final Transaction transaction = reference.get(); - if (transaction != null) { - reference.clear(); - transaction.close(); - } - } - } - - private void destroyAllSurfaceControls() { - for (final WeakReference<SurfaceControl> reference : mSurfaceControls) { - final SurfaceControl control = reference.get(); - if (control != null) { - reference.clear(); - control.destroy(); - } - } - } -} diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java deleted file mode 100644 index 343d35959df4..000000000000 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.junit.Assert.assertThat; - -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; - -import org.junit.Rule; -import org.junit.Test; - -/** - * Build/InstallRun: - * atest FrameworksServicesTests:WindowManagerServiceRuleTest - */ -@Presubmit -@SmallTest -public class WindowManagerServiceRuleTest { - - @Rule - public final WindowManagerServiceRule mRule = new WindowManagerServiceRule(); - - @Test - public void testWindowManagerSetUp() { - assertThat(mRule.getWindowManagerService(), notNullValue()); - } -} diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 5c3368bd25ac..3813891ef0e0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -53,6 +53,7 @@ import android.view.WindowManager; import com.android.server.AttributeCache; import org.junit.After; +import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; @@ -95,17 +96,22 @@ class WindowTestsBase { public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = new DexmakerShareClassLoaderRule(); - @Rule - public final WindowManagerServiceRule mWmRule = new WindowManagerServiceRule(); - - static WindowState.PowerManagerWrapper sPowerManagerWrapper; // TODO(roosa): make non-static. + static WindowState.PowerManagerWrapper sPowerManagerWrapper; @BeforeClass public static void setUpOnceBase() { AttributeCache.init(getInstrumentation().getTargetContext()); + + WmServiceUtils.setUpWindowManagerService(); + sPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class); } + @AfterClass + public static void tearDonwOnceBase() { + WmServiceUtils.tearDownWindowManagerService(); + } + @Before public void setUpBase() { // If @Before throws an exception, the error isn't logged. This will make sure any failures @@ -115,7 +121,7 @@ class WindowTestsBase { final Context context = getInstrumentation().getTargetContext(); - mWm = mWmRule.getWindowManagerService(); + mWm = WmServiceUtils.getWindowManagerService(); beforeCreateDisplay(); context.getDisplay().getDisplayInfo(mDisplayInfo); @@ -214,7 +220,7 @@ class WindowTestsBase { * Waits until the main handler for WM has processed all messages. */ void waitUntilHandlersIdle() { - mWmRule.waitUntilWindowManagerHandlersIdle(); + WmServiceUtils.waitUntilWindowManagerHandlersIdle(); } private WindowToken createWindowToken( diff --git a/services/tests/wmtests/src/com/android/server/wm/WmServiceUtils.java b/services/tests/wmtests/src/com/android/server/wm/WmServiceUtils.java new file mode 100644 index 000000000000..2465e5d89bb1 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/WmServiceUtils.java @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader; +import static android.view.Display.DEFAULT_DISPLAY; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.nullable; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; + +import android.app.ActivityManagerInternal; +import android.app.AppOpsManager; +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.IntentFilter; +import android.database.ContentObserver; +import android.hardware.display.DisplayManagerInternal; +import android.net.Uri; +import android.os.Handler; +import android.os.PowerManagerInternal; +import android.os.PowerSaveState; +import android.os.UserHandle; +import android.view.Display; +import android.view.InputChannel; + +import com.android.dx.mockito.inline.extended.StaticMockitoSession; +import com.android.server.LocalServices; +import com.android.server.LockGuard; +import com.android.server.Watchdog; +import com.android.server.input.InputManagerService; +import com.android.server.policy.WindowManagerPolicy; + +import org.mockito.invocation.InvocationOnMock; +import org.mockito.quality.Strictness; + +import java.util.concurrent.CountDownLatch; + +/** + * A Test utility class to create a mock {@link WindowManagerService} instance for tests. + */ +class WmServiceUtils { + private static StaticMockitoSession sMockitoSession; + private static WindowManagerService sService; + private static TestWindowManagerPolicy sPolicy; + + static void setUpWindowManagerService() { + sMockitoSession = mockitoSession() + .spyStatic(LockGuard.class) + .spyStatic(Watchdog.class) + .strictness(Strictness.LENIENT) + .startMocking(); + + runWithDexmakerShareClassLoader(WmServiceUtils::setUpTestWindowService); + } + + static void tearDownWindowManagerService() { + waitUntilWindowManagerHandlersIdle(); + removeLocalServices(); + sService = null; + sPolicy = null; + + sMockitoSession.finishMocking(); + } + + private static void setUpTestWindowService() { + doReturn(null).when(() -> LockGuard.installLock(any(), anyInt())); + doReturn(mock(Watchdog.class)).when(Watchdog::getInstance); + + final Context context = getInstrumentation().getTargetContext(); + spyOn(context); + + doReturn(null).when(context) + .registerReceiver(nullable(BroadcastReceiver.class), any(IntentFilter.class)); + doReturn(null).when(context) + .registerReceiverAsUser(any(BroadcastReceiver.class), any(UserHandle.class), + any(IntentFilter.class), nullable(String.class), nullable(Handler.class)); + + final ContentResolver contentResolver = context.getContentResolver(); + spyOn(contentResolver); + doNothing().when(contentResolver) + .registerContentObserver(any(Uri.class), anyBoolean(), any(ContentObserver.class), + anyInt()); + + final AppOpsManager appOpsManager = mock(AppOpsManager.class); + doReturn(appOpsManager).when(context) + .getSystemService(eq(Context.APP_OPS_SERVICE)); + + removeLocalServices(); + + final DisplayManagerInternal dmi = mock(DisplayManagerInternal.class); + LocalServices.addService(DisplayManagerInternal.class, dmi); + + final PowerManagerInternal pmi = mock(PowerManagerInternal.class); + LocalServices.addService(PowerManagerInternal.class, pmi); + final PowerSaveState state = new PowerSaveState.Builder().build(); + doReturn(state).when(pmi).getLowPowerState(anyInt()); + + final ActivityManagerInternal ami = mock(ActivityManagerInternal.class); + LocalServices.addService(ActivityManagerInternal.class, ami); + + final ActivityTaskManagerInternal atmi = mock(ActivityTaskManagerInternal.class); + LocalServices.addService(ActivityTaskManagerInternal.class, atmi); + doAnswer((InvocationOnMock invocationOnMock) -> { + final Runnable runnable = invocationOnMock.getArgument(0); + if (runnable != null) { + runnable.run(); + } + return null; + }).when(atmi).notifyKeyguardFlagsChanged(nullable(Runnable.class), anyInt()); + + final InputManagerService ims = mock(InputManagerService.class); + // InputChannel is final and can't be mocked. + final InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM); + if (input != null && input.length > 1) { + doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt()); + } + + final ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class); + final WindowManagerGlobalLock wmLock = new WindowManagerGlobalLock(); + doReturn(wmLock).when(atms).getGlobalLock(); + + sPolicy = new TestWindowManagerPolicy(WmServiceUtils::getWindowManagerService); + sService = WindowManagerService.main(context, ims, false, false, sPolicy, atms); + + sService.onInitReady(); + + final Display display = sService.mDisplayManager.getDisplay(DEFAULT_DISPLAY); + // Display creation is driven by the ActivityManagerService via + // ActivityStackSupervisor. We emulate those steps here. + sService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class)); + } + + private static void removeLocalServices() { + LocalServices.removeServiceForTest(DisplayManagerInternal.class); + LocalServices.removeServiceForTest(PowerManagerInternal.class); + LocalServices.removeServiceForTest(ActivityManagerInternal.class); + LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class); + LocalServices.removeServiceForTest(WindowManagerInternal.class); + LocalServices.removeServiceForTest(WindowManagerPolicy.class); + } + + static WindowManagerService getWindowManagerService() { + return sService; + } + + static void waitUntilWindowManagerHandlersIdle() { + final WindowManagerService wm = getWindowManagerService(); + if (wm == null) { + return; + } + // Removing delayed FORCE_GC message decreases time for waiting idle. + wm.mH.removeMessages(WindowManagerService.H.FORCE_GC); + waitHandlerIdle(wm.mH); + waitHandlerIdle(wm.mAnimationHandler); + waitHandlerIdle(SurfaceAnimationThread.getHandler()); + } + + private static void waitHandlerIdle(Handler handler) { + if (!handler.hasMessagesOrCallbacks()) { + return; + } + final CountDownLatch latch = new CountDownLatch(1); + // Wait for delayed messages are processed. + handler.getLooper().getQueue().addIdleHandler(() -> { + if (handler.hasMessagesOrCallbacks()) { + return true; // keep idle handler. + } + latch.countDown(); + return false; // remove idle handler. + }); + try { + latch.await(); + } catch (InterruptedException e) { + } + } +} diff --git a/services/usage/java/com/android/server/usage/AppTimeLimitController.java b/services/usage/java/com/android/server/usage/AppTimeLimitController.java index 8e1ede116abf..2ed11fe92e15 100644 --- a/services/usage/java/com/android/server/usage/AppTimeLimitController.java +++ b/services/usage/java/com/android/server/usage/AppTimeLimitController.java @@ -23,7 +23,6 @@ import android.os.Looper; import android.os.Message; import android.os.SystemClock; import android.util.ArrayMap; -import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; @@ -62,6 +61,8 @@ public class AppTimeLimitController { private static final long ONE_MINUTE = 60_000L; + private static final Integer ONE = new Integer(1); + /** Collection of data for each user that has reported usage */ @GuardedBy("mLock") private final SparseArray<UserData> mUsers = new SparseArray<>(); @@ -79,11 +80,11 @@ public class AppTimeLimitController { private @UserIdInt int userId; - /** Set of the currently active entities */ - private final ArraySet<String> currentlyActive = new ArraySet<>(); + /** Count of the currently active entities */ + public final ArrayMap<String, Integer> currentlyActive = new ArrayMap<>(); /** Map from entity name for quick lookup */ - private final ArrayMap<String, ArrayList<UsageGroup>> observedMap = new ArrayMap<>(); + public final ArrayMap<String, ArrayList<UsageGroup>> observedMap = new ArrayMap<>(); private UserData(@UserIdInt int userId) { this.userId = userId; @@ -94,7 +95,7 @@ public class AppTimeLimitController { // TODO: Consider using a bloom filter here if number of actives becomes large final int size = entities.length; for (int i = 0; i < size; i++) { - if (currentlyActive.contains(entities[i])) { + if (currentlyActive.containsKey(entities[i])) { return true; } } @@ -137,7 +138,7 @@ public class AppTimeLimitController { pw.print(" Currently Active:"); final int nActive = currentlyActive.size(); for (int i = 0; i < nActive; i++) { - pw.print(currentlyActive.valueAt(i)); + pw.print(currentlyActive.keyAt(i)); pw.print(", "); } pw.println(); @@ -233,6 +234,7 @@ public class AppTimeLimitController { protected long mUsageTimeMs; protected int mActives; protected long mLastKnownUsageTimeMs; + protected long mLastUsageEndTimeMs; protected WeakReference<UserData> mUserRef; protected WeakReference<ObserverAppData> mObserverAppRef; protected PendingIntent mLimitReachedCallback; @@ -271,9 +273,15 @@ public class AppTimeLimitController { @GuardedBy("mLock") void noteUsageStart(long startTimeMs, long currentTimeMs) { if (mActives++ == 0) { + // If last known usage ended after the start of this usage, there is overlap + // between the last usage session and this one. Avoid double counting by only + // counting from the end of the last session. This has a rare side effect that some + // usage will not be accounted for if the previous session started and stopped + // within this current usage. + startTimeMs = mLastUsageEndTimeMs > startTimeMs ? mLastUsageEndTimeMs : startTimeMs; mLastKnownUsageTimeMs = startTimeMs; final long timeRemaining = - mTimeLimitMs - mUsageTimeMs + currentTimeMs - startTimeMs; + mTimeLimitMs - mUsageTimeMs - currentTimeMs + startTimeMs; if (timeRemaining > 0) { if (DEBUG) { Slog.d(TAG, "Posting timeout for " + mObserverId + " for " @@ -287,7 +295,7 @@ public class AppTimeLimitController { mActives = mObserved.length; final UserData user = mUserRef.get(); if (user == null) return; - final Object[] array = user.currentlyActive.toArray(); + final Object[] array = user.currentlyActive.keySet().toArray(); Slog.e(TAG, "Too many noted usage starts! Observed entities: " + Arrays.toString( mObserved) + " Active Entities: " + Arrays.toString(array)); @@ -300,6 +308,8 @@ public class AppTimeLimitController { if (--mActives == 0) { final boolean limitNotCrossed = mUsageTimeMs < mTimeLimitMs; mUsageTimeMs += stopTimeMs - mLastKnownUsageTimeMs; + + mLastUsageEndTimeMs = stopTimeMs; if (limitNotCrossed && mUsageTimeMs >= mTimeLimitMs) { // Crossed the limit if (DEBUG) Slog.d(TAG, "MTB informing group obs=" + mObserverId); @@ -312,7 +322,7 @@ public class AppTimeLimitController { mActives = 0; final UserData user = mUserRef.get(); if (user == null) return; - final Object[] array = user.currentlyActive.toArray(); + final Object[] array = user.currentlyActive.keySet().toArray(); Slog.e(TAG, "Too many noted usage stops! Observed entities: " + Arrays.toString( mObserved) + " Active Entities: " + Arrays.toString(array)); @@ -409,7 +419,6 @@ public class AppTimeLimitController { } class SessionUsageGroup extends UsageGroup { - private long mLastUsageEndTimeMs; private long mNewSessionThresholdMs; private PendingIntent mSessionEndCallback; @@ -451,7 +460,6 @@ public class AppTimeLimitController { public void noteUsageStop(long stopTimeMs) { super.noteUsageStop(stopTimeMs); if (mActives == 0) { - mLastUsageEndTimeMs = stopTimeMs; if (mUsageTimeMs >= mTimeLimitMs) { // Usage has ended. Schedule the session end callback to be triggered once // the new session threshold has been reached @@ -467,7 +475,10 @@ public class AppTimeLimitController { UserData user = mUserRef.get(); if (user == null) return; if (mListener != null) { - mListener.onSessionEnd(mObserverId, user.userId, mUsageTimeMs, mSessionEndCallback); + mListener.onSessionEnd(mObserverId, + user.userId, + mUsageTimeMs, + mSessionEndCallback); } } @@ -599,7 +610,7 @@ public class AppTimeLimitController { // TODO: Consider using a bloom filter here if number of actives becomes large final int size = group.mObserved.length; for (int i = 0; i < size; i++) { - if (user.currentlyActive.contains(group.mObserved[i])) { + if (user.currentlyActive.containsKey(group.mObserved[i])) { // Entity is currently active. Start group's usage. group.noteUsageStart(currentTimeMs); } @@ -717,21 +728,28 @@ public class AppTimeLimitController { /** * Called when an entity becomes active. * - * @param name The entity that became active - * @param userId The user + * @param name The entity that became active + * @param userId The user + * @param timeAgoMs Time since usage was started */ - public void noteUsageStart(String name, int userId) throws IllegalArgumentException { + public void noteUsageStart(String name, int userId, long timeAgoMs) + throws IllegalArgumentException { synchronized (mLock) { UserData user = getOrCreateUserDataLocked(userId); if (DEBUG) Slog.d(TAG, "Usage entity " + name + " became active"); - if (user.currentlyActive.contains(name)) { - throw new IllegalArgumentException( - "Unable to start usage for " + name + ", already in use"); + + final int index = user.currentlyActive.indexOfKey(name); + if (index >= 0) { + final Integer count = user.currentlyActive.valueAt(index); + if (count != null) { + // There are multiple instances of this entity. Just increment the count. + user.currentlyActive.setValueAt(index, count + 1); + return; + } } final long currentTime = getUptimeMillis(); - // Add to the list of active entities - user.currentlyActive.add(name); + user.currentlyActive.put(name, ONE); ArrayList<UsageGroup> groups = user.observedMap.get(name); if (groups == null) return; @@ -739,12 +757,22 @@ public class AppTimeLimitController { final int size = groups.size(); for (int i = 0; i < size; i++) { UsageGroup group = groups.get(i); - group.noteUsageStart(currentTime); + group.noteUsageStart(currentTime - timeAgoMs, currentTime); } } } /** + * Called when an entity becomes active. + * + * @param name The entity that became active + * @param userId The user + */ + public void noteUsageStart(String name, int userId) throws IllegalArgumentException { + noteUsageStart(name, userId, 0); + } + + /** * Called when an entity becomes inactive. * * @param name The entity that became inactive @@ -754,10 +782,21 @@ public class AppTimeLimitController { synchronized (mLock) { UserData user = getOrCreateUserDataLocked(userId); if (DEBUG) Slog.d(TAG, "Usage entity " + name + " became inactive"); - if (!user.currentlyActive.remove(name)) { + + final int index = user.currentlyActive.indexOfKey(name); + if (index < 0) { throw new IllegalArgumentException( "Unable to stop usage for " + name + ", not in use"); } + + final Integer count = user.currentlyActive.valueAt(index); + if (!count.equals(ONE)) { + // There are multiple instances of this entity. Just decrement the count. + user.currentlyActive.setValueAt(index, count - 1); + return; + } + + user.currentlyActive.removeAt(index); final long currentTime = getUptimeMillis(); // Check if any of the groups need to watch for this entity @@ -769,6 +808,7 @@ public class AppTimeLimitController { UsageGroup group = groups.get(i); group.noteUsageStop(currentTime); } + } } @@ -780,7 +820,8 @@ public class AppTimeLimitController { @GuardedBy("mLock") private void postInformSessionEndListenerLocked(SessionUsageGroup group, long timeout) { - mHandler.sendMessageDelayed(mHandler.obtainMessage(MyHandler.MSG_INFORM_SESSION_END, group), + mHandler.sendMessageDelayed( + mHandler.obtainMessage(MyHandler.MSG_INFORM_SESSION_END, group), timeout); } @@ -800,7 +841,27 @@ public class AppTimeLimitController { mHandler.removeMessages(MyHandler.MSG_CHECK_TIMEOUT, group); } - void dump(PrintWriter pw) { + void dump(String[] args, PrintWriter pw) { + if (args != null) { + for (int i = 0; i < args.length; i++) { + String arg = args[i]; + if ("actives".equals(arg)) { + synchronized (mLock) { + final int nUsers = mUsers.size(); + for (int user = 0; user < nUsers; user++) { + final ArrayMap<String, Integer> actives = + mUsers.valueAt(user).currentlyActive; + final int nActive = actives.size(); + for (int active = 0; active < nActive; active++) { + pw.println(actives.keyAt(active)); + } + } + } + return; + } + } + } + synchronized (mLock) { pw.println("\n App Time Limits"); final int nUsers = mUsers.size(); diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 57dc08fcd253..f146370b01d7 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -53,6 +53,7 @@ import android.os.Binder; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; +import android.os.IBinder; import android.os.IDeviceIdleController; import android.os.Looper; import android.os.Message; @@ -105,6 +106,8 @@ public class UsageStatsService extends SystemService implements private static final boolean ENABLE_KERNEL_UPDATES = true; private static final File KERNEL_COUNTER_FILE = new File("/proc/uid_procstat/set"); + private static final char TOKEN_DELIMITER = '/'; + // Handler message types. static final int MSG_REPORT_EVENT = 0; static final int MSG_FLUSH_TO_DISK = 1; @@ -135,6 +138,10 @@ public class UsageStatsService extends SystemService implements /** Manages app time limit observers */ AppTimeLimitController mAppTimeLimit; + final SparseArray<ArraySet<String>> mUsageReporters = new SparseArray(); + final SparseArray<String> mVisibleActivities = new SparseArray(); + + private UsageStatsManagerInternal.AppIdleStateChangeListener mStandbyChangeListener = new UsageStatsManagerInternal.AppIdleStateChangeListener() { @Override @@ -270,7 +277,7 @@ public class UsageStatsService extends SystemService implements mHandler.obtainMessage(MSG_REMOVE_USER, userId, 0).sendToTarget(); } } else if (Intent.ACTION_USER_STARTED.equals(action)) { - if (userId >=0) { + if (userId >= 0) { mAppStandby.postCheckIdleStates(userId); } } @@ -434,17 +441,46 @@ public class UsageStatsService extends SystemService implements mAppStandby.reportEvent(event, elapsedRealtime, userId); switch (event.mEventType) { case Event.ACTIVITY_RESUMED: - try { - mAppTimeLimit.noteUsageStart(event.getPackageName(), userId); - } catch (IllegalArgumentException iae) { - Slog.e(TAG, "Failed to note usage start", iae); + synchronized (mVisibleActivities) { + mVisibleActivities.put(event.mInstanceId, event.getClassName()); + try { + mAppTimeLimit.noteUsageStart(event.getPackageName(), userId); + } catch (IllegalArgumentException iae) { + Slog.e(TAG, "Failed to note usage start", iae); + } } break; - case Event.ACTIVITY_PAUSED: - try { - mAppTimeLimit.noteUsageStop(event.getPackageName(), userId); - } catch (IllegalArgumentException iae) { - Slog.e(TAG, "Failed to note usage stop", iae); + case Event.ACTIVITY_STOPPED: + case Event.ACTIVITY_DESTROYED: + ArraySet<String> tokens; + synchronized (mUsageReporters) { + tokens = mUsageReporters.removeReturnOld(event.mInstanceId); + } + if (tokens != null) { + synchronized (tokens) { + final int size = tokens.size(); + // Stop usage on behalf of a UsageReporter that stopped + for (int i = 0; i < size; i++) { + final String token = tokens.valueAt(i); + try { + mAppTimeLimit.noteUsageStop( + buildFullToken(event.getPackageName(), token), userId); + } catch (IllegalArgumentException iae) { + Slog.w(TAG, "Failed to stop usage for during reporter death: " + + iae); + } + } + } + } + + synchronized (mVisibleActivities) { + if (mVisibleActivities.removeReturnOld(event.mInstanceId) != null) { + try { + mAppTimeLimit.noteUsageStop(event.getPackageName(), userId); + } catch (IllegalArgumentException iae) { + Slog.w(TAG, "Failed to note usage stop", iae); + } + } } break; } @@ -599,6 +635,14 @@ public class UsageStatsService extends SystemService implements return beginTime <= currentTime && beginTime < endTime; } + private String buildFullToken(String packageName, String token) { + final StringBuilder sb = new StringBuilder(packageName.length() + token.length() + 1); + sb.append(packageName); + sb.append(TOKEN_DELIMITER); + sb.append(token); + return sb.toString(); + } + private void flushToDiskLocked() { final int userCount = mUserState.size(); for (int i = 0; i < userCount; i++) { @@ -627,8 +671,7 @@ public class UsageStatsService extends SystemService implements String arg = args[i]; if ("--checkin".equals(arg)) { checkin = true; - } else - if ("-c".equals(arg)) { + } else if ("-c".equals(arg)) { compact = true; } else if ("flush".equals(arg)) { flushToDiskLocked(); @@ -637,6 +680,15 @@ public class UsageStatsService extends SystemService implements } else if ("is-app-standby-enabled".equals(arg)) { pw.println(mAppStandby.mAppIdleEnabled); return; + } else if ("apptimelimit".equals(arg)) { + if (i + 1 >= args.length) { + mAppTimeLimit.dump(null, pw); + } else { + final String[] remainingArgs = + Arrays.copyOfRange(args, i + 1, args.length); + mAppTimeLimit.dump(remainingArgs, pw); + } + return; } else if (arg != null && !arg.startsWith("-")) { // Anything else that doesn't start with '-' is a pkg to filter pkg = arg; @@ -666,7 +718,7 @@ public class UsageStatsService extends SystemService implements mAppStandby.dumpState(args, pw); } - mAppTimeLimit.dump(pw); + mAppTimeLimit.dump(null, pw); } } @@ -1231,16 +1283,82 @@ public class UsageStatsService extends SystemService implements final int userId = UserHandle.getUserId(callingUid); final long token = Binder.clearCallingIdentity(); try { - UsageStatsService.this.unregisterUsageSessionObserver(callingUid, sessionObserverId, userId); + UsageStatsService.this.unregisterUsageSessionObserver(callingUid, sessionObserverId, + userId); } finally { Binder.restoreCallingIdentity(token); } } + + @Override + public void reportUsageStart(IBinder activity, String token, String callingPackage) { + reportPastUsageStart(activity, token, 0, callingPackage); + } + + @Override + public void reportPastUsageStart(IBinder activity, String token, long timeAgoMs, + String callingPackage) { + + final int callingUid = Binder.getCallingUid(); + final int userId = UserHandle.getUserId(callingUid); + final long binderToken = Binder.clearCallingIdentity(); + try { + ArraySet<String> tokens; + synchronized (mUsageReporters) { + tokens = mUsageReporters.get(activity.hashCode()); + if (tokens == null) { + tokens = new ArraySet(); + mUsageReporters.put(activity.hashCode(), tokens); + } + } + + synchronized (tokens) { + if (!tokens.add(token)) { + throw new IllegalArgumentException(token + " for " + callingPackage + + " is already reported as started for this activity"); + } + } + + mAppTimeLimit.noteUsageStart(buildFullToken(callingPackage, token), + userId, timeAgoMs); + } finally { + Binder.restoreCallingIdentity(binderToken); + } + } + + @Override + public void reportUsageStop(IBinder activity, String token, String callingPackage) { + final int callingUid = Binder.getCallingUid(); + final int userId = UserHandle.getUserId(callingUid); + final long binderToken = Binder.clearCallingIdentity(); + try { + ArraySet<String> tokens; + synchronized (mUsageReporters) { + tokens = mUsageReporters.get(activity.hashCode()); + if (tokens == null) { + throw new IllegalArgumentException( + "Unknown reporter trying to stop token " + token + " for " + + callingPackage); + } + } + + synchronized (tokens) { + if (!tokens.remove(token)) { + throw new IllegalArgumentException(token + " for " + callingPackage + + " is already reported as stopped for this activity"); + } + } + mAppTimeLimit.noteUsageStop(buildFullToken(callingPackage, token), userId); + } finally { + Binder.restoreCallingIdentity(binderToken); + } + } } void registerAppUsageObserver(int callingUid, int observerId, String[] packages, long timeLimitMs, PendingIntent callbackIntent, int userId) { - mAppTimeLimit.addAppUsageObserver(callingUid, observerId, packages, timeLimitMs, callbackIntent, + mAppTimeLimit.addAppUsageObserver(callingUid, observerId, packages, timeLimitMs, + callbackIntent, userId); } diff --git a/startop/iorap/Android.bp b/startop/iorap/Android.bp index b3b09001e7ee..59a80fbae792 100644 --- a/startop/iorap/Android.bp +++ b/startop/iorap/Android.bp @@ -13,7 +13,7 @@ // limitations under the License. java_library_static { - name: "libiorap-java", + name: "services.startop.iorap", aidl: { include_dirs: [ @@ -21,6 +21,8 @@ java_library_static { ], }, + libs: ["services.core"], + srcs: [ ":iorap-aidl", "**/*.java", diff --git a/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java b/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java new file mode 100644 index 000000000000..c2e4581285fd --- /dev/null +++ b/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java @@ -0,0 +1,368 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.startop.iorap; + +import android.annotation.LongDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.os.Parcel; +import android.os.Parcelable; + +// TODO: fix this. either move this class into system server or add a dependency on +// these wm classes to libiorap-java and libiorap-java-tests (somehow). +import com.android.server.wm.ActivityMetricsLaunchObserver; +import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto; +import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.InvocationTargetException; +import java.util.Objects; + +/** + * Provide a hint to iorapd that an app launch sequence has transitioned state.<br /><br /> + * + * Knowledge of when an activity starts/stops can be used by iorapd to increase system + * performance (e.g. by launching perfetto tracing to record an io profile, or by + * playing back an ioprofile via readahead) over the long run.<br /><br /> + * + * /@see com.google.android.startop.iorap.IIorap#onAppLaunchEvent <br /><br /> + * @see com.android.server.wm.ActivityMetricsLaunchObserver + * ActivityMetricsLaunchObserver for the possible event states. + * @hide + */ +public abstract class AppLaunchEvent implements Parcelable { + @LongDef + @Retention(RetentionPolicy.SOURCE) + public @interface SequenceId {} + + public final @SequenceId + long sequenceId; + + protected AppLaunchEvent(@SequenceId long sequenceId) { + this.sequenceId = sequenceId; + } + + @Override + public boolean equals(Object other) { + if (other instanceof AppLaunchEvent) { + return equals((AppLaunchEvent) other); + } + return false; + } + + protected boolean equals(AppLaunchEvent other) { + return sequenceId == other.sequenceId; + } + + + @Override + public String toString() { + return getClass().getSimpleName() + + "{" + "sequenceId=" + Long.toString(sequenceId) + + toStringBody() + "}"; + } + + protected String toStringBody() { return ""; }; + + // List of possible variants: + + public static final class IntentStarted extends AppLaunchEvent { + @NonNull + public final Intent intent; + + public IntentStarted(@SequenceId long sequenceId, Intent intent) { + super(sequenceId); + this.intent = intent; + + Objects.requireNonNull(intent, "intent"); + } + + @Override + public boolean equals(Object other) { + if (other instanceof IntentStarted) { + return intent.equals(((IntentStarted)other).intent) && + super.equals(other); + } + return false; + } + + @Override + protected String toStringBody() { + return ", intent=" + intent.toString(); + } + + + @Override + protected void writeToParcelImpl(Parcel p, int flags) { + super.writeToParcelImpl(p, flags); + intent.writeToParcel(p, flags); + } + + IntentStarted(Parcel p) { + super(p); + intent = Intent.CREATOR.createFromParcel(p); + } + } + + public static final class IntentFailed extends AppLaunchEvent { + public IntentFailed(@SequenceId long sequenceId) { + super(sequenceId); + } + + @Override + public boolean equals(Object other) { + if (other instanceof IntentFailed) { + return super.equals(other); + } + return false; + } + + IntentFailed(Parcel p) { + super(p); + } + } + + public static abstract class BaseWithActivityRecordData extends AppLaunchEvent { + public final @NonNull + @ActivityRecordProto byte[] activityRecordSnapshot; + + protected BaseWithActivityRecordData(@SequenceId long sequenceId, + @NonNull @ActivityRecordProto byte[] snapshot) { + super(sequenceId); + activityRecordSnapshot = snapshot; + + Objects.requireNonNull(snapshot, "snapshot"); + } + + @Override + public boolean equals(Object other) { + if (other instanceof BaseWithActivityRecordData) { + return activityRecordSnapshot.equals( + ((BaseWithActivityRecordData)other).activityRecordSnapshot) && + super.equals(other); + } + return false; + } + + @Override + protected String toStringBody() { + return ", " + activityRecordSnapshot.toString(); + } + + @Override + protected void writeToParcelImpl(Parcel p, int flags) { + super.writeToParcelImpl(p, flags); + ActivityRecordProtoParcelable.write(p, activityRecordSnapshot, flags); + } + + BaseWithActivityRecordData(Parcel p) { + super(p); + activityRecordSnapshot = ActivityRecordProtoParcelable.create(p); + } + } + + public static final class ActivityLaunched extends BaseWithActivityRecordData { + public final @ActivityMetricsLaunchObserver.Temperature + int temperature; + + public ActivityLaunched(@SequenceId long sequenceId, + @NonNull @ActivityRecordProto byte[] snapshot, + @ActivityMetricsLaunchObserver.Temperature int temperature) { + super(sequenceId, snapshot); + this.temperature = temperature; + } + + @Override + public boolean equals(Object other) { + if (other instanceof ActivityLaunched) { + return temperature == ((ActivityLaunched)other).temperature && + super.equals(other); + } + return false; + } + + @Override + protected String toStringBody() { + return ", temperature=" + Integer.toString(temperature); + } + + @Override + protected void writeToParcelImpl(Parcel p, int flags) { + super.writeToParcelImpl(p, flags); + p.writeInt(temperature); + } + + ActivityLaunched(Parcel p) { + super(p); + temperature = p.readInt(); + } + } + + public static final class ActivityLaunchFinished extends BaseWithActivityRecordData { + public ActivityLaunchFinished(@SequenceId long sequenceId, + @NonNull @ActivityRecordProto byte[] snapshot) { + super(sequenceId, snapshot); + } + + @Override + public boolean equals(Object other) { + if (other instanceof ActivityLaunched) { + return super.equals(other); + } + return false; + } + } + + public static class ActivityLaunchCancelled extends AppLaunchEvent { + public final @Nullable + @ActivityRecordProto byte[] activityRecordSnapshot; + + public ActivityLaunchCancelled(@SequenceId long sequenceId, + @Nullable @ActivityRecordProto byte[] snapshot) { + super(sequenceId); + activityRecordSnapshot = snapshot; + } + + @Override + public boolean equals(Object other) { + if (other instanceof ActivityLaunchCancelled) { + return Objects.equals(activityRecordSnapshot, + ((ActivityLaunchCancelled)other).activityRecordSnapshot) && + super.equals(other); + } + return false; + } + + @Override + protected String toStringBody() { + return ", " + activityRecordSnapshot.toString(); + } + + @Override + protected void writeToParcelImpl(Parcel p, int flags) { + super.writeToParcelImpl(p, flags); + if (activityRecordSnapshot != null) { + p.writeBoolean(true); + ActivityRecordProtoParcelable.write(p, activityRecordSnapshot, flags); + } else { + p.writeBoolean(false); + } + } + + ActivityLaunchCancelled(Parcel p) { + super(p); + if (p.readBoolean()) { + activityRecordSnapshot = ActivityRecordProtoParcelable.create(p); + } else { + activityRecordSnapshot = null; + } + } + } + + @Override + public @ContentsFlags int describeContents() { return 0; } + + @Override + public void writeToParcel(Parcel p, @WriteFlags int flags) { + p.writeInt(getTypeIndex()); + + writeToParcelImpl(p, flags); + } + + + public static Creator<AppLaunchEvent> CREATOR = + new Creator<AppLaunchEvent>() { + @Override + public AppLaunchEvent createFromParcel(Parcel source) { + int typeIndex = source.readInt(); + + Class<?> kls = getClassFromTypeIndex(typeIndex); + if (kls == null) { + throw new IllegalArgumentException("Invalid type index: " + typeIndex); + } + + try { + return (AppLaunchEvent) kls.getConstructor(Parcel.class).newInstance(source); + } catch (InstantiationException e) { + throw new AssertionError(e); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } catch (InvocationTargetException e) { + throw new AssertionError(e); + } catch (NoSuchMethodException e) { + throw new AssertionError(e); + } + } + + @Override + public AppLaunchEvent[] newArray(int size) { + return new AppLaunchEvent[0]; + } + }; + + protected void writeToParcelImpl(Parcel p, int flags) { + p.writeLong(sequenceId); + } + + protected AppLaunchEvent(Parcel p) { + sequenceId = p.readLong(); + } + + private int getTypeIndex() { + for (int i = 0; i < sTypes.length; ++i) { + if (sTypes[i].equals(this.getClass())) { + return i; + } + } + throw new AssertionError("sTypes did not include this type: " + this.getClass()); + } + + private static @Nullable Class<?> getClassFromTypeIndex(int typeIndex) { + if (typeIndex >= 0 && typeIndex < sTypes.length) { + return sTypes[typeIndex]; + } + return null; + } + + // Index position matters: It is used to encode the specific type in parceling. + // Keep up-to-date with C++ side. + private static Class<?>[] sTypes = new Class[] { + IntentStarted.class, + IntentFailed.class, + ActivityLaunched.class, + ActivityLaunchFinished.class, + ActivityLaunchCancelled.class, + }; + + // TODO: move to @ActivityRecordProto byte[] once we have unit tests. + public static class ActivityRecordProtoParcelable { + public static void write(Parcel p, @ActivityRecordProto byte[] activityRecordSnapshot, + int flags) { + p.writeByteArray(activityRecordSnapshot); + } + + public static @ActivityRecordProto byte[] create(Parcel p) { + byte[] data = p.createByteArray(); + + return data; + } + } +} diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java new file mode 100644 index 000000000000..7fcad360b8fe --- /dev/null +++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.startop.iorap; +// TODO: rename to com.android.server.startop.iorap + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.os.IBinder; +import android.os.Parcel; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.LocalServices; +import com.android.server.SystemService; +import com.android.server.wm.ActivityMetricsLaunchObserver; +import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto; +import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature; +import com.android.server.wm.ActivityMetricsLaunchObserverRegistry; +import com.android.server.wm.ActivityTaskManagerInternal; + +/** + * System-server-local proxy into the {@code IIorap} native service. + */ +public class IorapForwardingService extends SystemService { + + public static final boolean DEBUG = true; // TODO: read from a getprop? + public static final String TAG = "IorapForwardingService"; + + private IIorap mIorapRemote; + + /** + * Initializes the system service. + * <p> + * Subclasses must define a single argument constructor that accepts the context + * and passes it to super. + * </p> + * + * @param context The system server context. + */ + public IorapForwardingService(Context context) { + super(context); + } + + //<editor-fold desc="Providers"> + /* + * Providers for external dependencies: + * - These are marked as protected to allow tests to inject different values via mocks. + */ + + @VisibleForTesting + protected ActivityMetricsLaunchObserverRegistry provideLaunchObserverRegistry() { + ActivityTaskManagerInternal amtInternal = + LocalServices.getService(ActivityTaskManagerInternal.class); + ActivityMetricsLaunchObserverRegistry launchObserverRegistry = + amtInternal.getLaunchObserverRegistry(); + return launchObserverRegistry; + } + + @VisibleForTesting + protected IIorap provideIorapRemote() { + try { + return IIorap.Stub.asInterface(ServiceManager.getServiceOrThrow("iorapd")); + } catch (ServiceManager.ServiceNotFoundException e) { + // TODO: how do we handle service being missing? + throw new AssertionError(e); + } + } + + //</editor-fold> + + @Override + public void onStart() { + if (DEBUG) { + Log.v(TAG, "onStart"); + } + + // Connect to the native binder service. + mIorapRemote = provideIorapRemote(); + invokeRemote( () -> mIorapRemote.setTaskListener(new RemoteTaskListener()) ); + + // Listen to App Launch Sequence events from ActivityTaskManager, + // and forward them to the native binder service. + ActivityMetricsLaunchObserverRegistry launchObserverRegistry = + provideLaunchObserverRegistry(); + launchObserverRegistry.registerLaunchObserver(new AppLaunchObserver()); + } + + private class AppLaunchObserver implements ActivityMetricsLaunchObserver { + // We add a synthetic sequence ID here to make it easier to differentiate new + // launch sequences on the native side. + private @AppLaunchEvent.SequenceId long mSequenceId = -1; + + @Override + public void onIntentStarted(@NonNull Intent intent) { + // #onIntentStarted [is the only transition that] initiates a new launch sequence. + ++mSequenceId; + + if (DEBUG) { + Log.v(TAG, String.format("AppLaunchObserver#onIntentStarted(%d, %s)", + mSequenceId, intent)); + } + + invokeRemote(() -> + mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(), + new AppLaunchEvent.IntentStarted(mSequenceId, intent)) + ); + } + + @Override + public void onIntentFailed() { + if (DEBUG) { + Log.v(TAG, String.format("AppLaunchObserver#onIntentFailed(%d)", mSequenceId)); + } + + invokeRemote(() -> + mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(), + new AppLaunchEvent.IntentFailed(mSequenceId)) + ); + } + + @Override + public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity, + @Temperature int temperature) { + if (DEBUG) { + Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunched(%d, %s, %d)", + mSequenceId, activity, temperature)); + } + + invokeRemote(() -> + mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(), + new AppLaunchEvent.ActivityLaunched(mSequenceId, activity, temperature)) + ); + } + + @Override + public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] activity) { + if (DEBUG) { + Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchCancelled(%d, %s)", + mSequenceId, activity)); + } + + invokeRemote(() -> + mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(), + new AppLaunchEvent.ActivityLaunchCancelled(mSequenceId, + activity))); + } + + @Override + public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity) { + if (DEBUG) { + Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchFinished(%d, %s)", + mSequenceId, activity)); + } + + invokeRemote(() -> + mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(), + new AppLaunchEvent.ActivityLaunchCancelled(mSequenceId, activity)) + ); + } + } + + private class RemoteTaskListener extends ITaskListener.Stub { + @Override + public void onProgress(RequestId requestId, TaskResult result) throws RemoteException { + if (DEBUG) { + Log.v(TAG, + String.format("RemoteTaskListener#onProgress(%s, %s)", requestId, result)); + } + + // TODO: implement rest. + } + + @Override + public void onComplete(RequestId requestId, TaskResult result) throws RemoteException { + if (DEBUG) { + Log.v(TAG, + String.format("RemoteTaskListener#onComplete(%s, %s)", requestId, result)); + } + + // TODO: implement rest. + } + } + + private interface RemoteRunnable { + void run() throws RemoteException; + } + + private static void invokeRemote(RemoteRunnable r) { + try { + r.run(); + } catch (RemoteException e) { + // TODO: what do we do with exceptions? + throw new AssertionError("not implemented", e); + } + } +} diff --git a/startop/iorap/src/com/google/android/startop/iorap/RequestId.java b/startop/iorap/src/com/google/android/startop/iorap/RequestId.java index 2c79319a1459..adb3a910f7fe 100644 --- a/startop/iorap/src/com/google/android/startop/iorap/RequestId.java +++ b/startop/iorap/src/com/google/android/startop/iorap/RequestId.java @@ -71,7 +71,7 @@ public class RequestId implements Parcelable { @Override public String toString() { - return String.format("{requestId: %ld}", requestId); + return String.format("{requestId: %d}", requestId); } @Override diff --git a/startop/iorap/tests/Android.bp b/startop/iorap/tests/Android.bp index 76057846e896..5ac4a46b81f1 100644 --- a/startop/iorap/tests/Android.bp +++ b/startop/iorap/tests/Android.bp @@ -18,8 +18,15 @@ java_library { srcs: ["src/**/*.kt"], static_libs: [ - // non-test dependencies - "libiorap-java", + // Non-test dependencies + + // library under test + "services.startop.iorap", + // need the system_server code to be on the classpath, + "services.core", + + // Test Dependencies + // test android dependencies "platform-test-annotations", "android-support-test", diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp index 82056e9a33fe..2fc3a0dd874e 100644 --- a/startop/view_compiler/Android.bp +++ b/startop/view_compiler/Android.bp @@ -22,17 +22,35 @@ cc_defaults { shared_libs: [ "libbase", "libdexfile", + "libz", "slicer", ], static_libs: [ "libtinyxml2", + "liblog", + "libutils", + "libziparchive", ], + cppflags: ["-std=c++17"], + target: { + android: { + shared_libs: [ + "libandroidfw", + ], + }, + host: { + static_libs: [ + "libandroidfw", + ], + }, + }, } cc_library_host_static { name: "libviewcompiler", defaults: ["viewcompiler_defaults"], srcs: [ + "apk_layout_compiler.cc", "dex_builder.cc", "dex_layout_compiler.cc", "java_lang_builder.cc", diff --git a/startop/view_compiler/apk_layout_compiler.cc b/startop/view_compiler/apk_layout_compiler.cc new file mode 100644 index 000000000000..e95041ba34a4 --- /dev/null +++ b/startop/view_compiler/apk_layout_compiler.cc @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "apk_layout_compiler.h" +#include "dex_layout_compiler.h" +#include "java_lang_builder.h" +#include "layout_validation.h" +#include "util.h" + +#include "androidfw/ApkAssets.h" +#include "androidfw/AssetManager2.h" +#include "androidfw/ResourceTypes.h" + +#include <iostream> +#include <locale> + +#include "android-base/stringprintf.h" + +namespace startop { + +using android::ResXMLParser; +using android::base::StringPrintf; + +class ResXmlVisitorAdapter { + public: + ResXmlVisitorAdapter(ResXMLParser* parser) : parser_{parser} {} + + template <typename Visitor> + void Accept(Visitor* visitor) { + size_t depth{0}; + do { + switch (parser_->next()) { + case ResXMLParser::START_DOCUMENT: + depth++; + visitor->VisitStartDocument(); + break; + case ResXMLParser::END_DOCUMENT: + depth--; + visitor->VisitEndDocument(); + break; + case ResXMLParser::START_TAG: { + depth++; + size_t name_length = 0; + const char16_t* name = parser_->getElementName(&name_length); + visitor->VisitStartTag(std::u16string{name, name_length}); + break; + } + case ResXMLParser::END_TAG: + depth--; + visitor->VisitEndTag(); + break; + default:; + } + } while (depth > 0 || parser_->getEventType() == ResXMLParser::FIRST_CHUNK_CODE); + } + + private: + ResXMLParser* parser_; +}; + +bool CanCompileLayout(ResXMLParser* parser) { + ResXmlVisitorAdapter adapter{parser}; + LayoutValidationVisitor visitor; + adapter.Accept(&visitor); + + return visitor.can_compile(); +} + +void CompileApkLayouts(const std::string& filename, CompilationTarget target, + std::ostream& target_out) { + auto assets = android::ApkAssets::Load(filename); + android::AssetManager2 resources; + resources.SetApkAssets({assets.get()}); + + std::string package_name; + + // TODO: handle multiple packages better + bool first = true; + for (const auto& package : assets->GetLoadedArsc()->GetPackages()) { + CHECK(first); + package_name = package->GetPackageName(); + first = false; + } + + dex::DexBuilder dex_file; + dex::ClassBuilder compiled_view{ + dex_file.MakeClass(StringPrintf("%s.CompiledView", package_name.c_str()))}; + std::vector<dex::MethodBuilder> methods; + + assets->ForEachFile("res/", [&](const android::StringPiece& s, android::FileType) { + if (s == "layout") { + auto path = StringPrintf("res/%s/", s.to_string().c_str()); + assets->ForEachFile(path, [&](const android::StringPiece& layout_file, android::FileType) { + auto layout_path = StringPrintf("%s%s", path.c_str(), layout_file.to_string().c_str()); + android::ApkAssetsCookie cookie = android::kInvalidCookie; + auto asset = resources.OpenNonAsset(layout_path, android::Asset::ACCESS_RANDOM, &cookie); + CHECK(asset); + CHECK(android::kInvalidCookie != cookie); + const auto dynamic_ref_table = resources.GetDynamicRefTableForCookie(cookie); + CHECK(nullptr != dynamic_ref_table); + android::ResXMLTree xml_tree{dynamic_ref_table}; + xml_tree.setTo(asset->getBuffer(/*wordAligned=*/true), + asset->getLength(), + /*copy_data=*/true); + android::ResXMLParser parser{xml_tree}; + parser.restart(); + if (CanCompileLayout(&parser)) { + parser.restart(); + const std::string layout_name = startop::util::FindLayoutNameFromFilename(layout_path); + ResXmlVisitorAdapter adapter{&parser}; + switch (target) { + case CompilationTarget::kDex: { + methods.push_back(compiled_view.CreateMethod( + layout_name, + dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.View"), + dex::TypeDescriptor::FromClassname("android.content.Context"), + dex::TypeDescriptor::Int()})); + DexViewBuilder builder(&methods.back()); + builder.Start(); + LayoutCompilerVisitor visitor{&builder}; + adapter.Accept(&visitor); + builder.Finish(); + methods.back().Encode(); + break; + } + case CompilationTarget::kJavaLanguage: { + JavaLangViewBuilder builder{package_name, layout_name, target_out}; + builder.Start(); + LayoutCompilerVisitor visitor{&builder}; + adapter.Accept(&visitor); + builder.Finish(); + break; + } + } + } + }); + } + }); + + if (target == CompilationTarget::kDex) { + slicer::MemView image{dex_file.CreateImage()}; + target_out.write(image.ptr<const char>(), image.size()); + } +} + +} // namespace startop diff --git a/startop/view_compiler/apk_layout_compiler.h b/startop/view_compiler/apk_layout_compiler.h new file mode 100644 index 000000000000..c85ddd65ac1b --- /dev/null +++ b/startop/view_compiler/apk_layout_compiler.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2018 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. + */ + +#ifndef APK_LAYOUT_COMPILER_H_ +#define APK_LAYOUT_COMPILER_H_ + +#include <string> + +namespace startop { + +enum class CompilationTarget { kJavaLanguage, kDex }; + +void CompileApkLayouts(const std::string& filename, CompilationTarget target, + std::ostream& target_out); + +} // namespace startop + +#endif // APK_LAYOUT_COMPILER_H_
\ No newline at end of file diff --git a/startop/view_compiler/main.cc b/startop/view_compiler/main.cc index ae00187e1908..871a421cee2d 100644 --- a/startop/view_compiler/main.cc +++ b/startop/view_compiler/main.cc @@ -17,6 +17,7 @@ #include "gflags/gflags.h" #include "android-base/stringprintf.h" +#include "apk_layout_compiler.h" #include "dex_builder.h" #include "dex_layout_compiler.h" #include "java_lang_builder.h" @@ -46,6 +47,7 @@ using std::string; constexpr char kStdoutFilename[]{"stdout"}; +DEFINE_bool(apk, false, "Compile layouts in an APK"); DEFINE_bool(dex, false, "Generate a DEX file instead of Java"); DEFINE_string(out, kStdoutFilename, "Where to write the generated class"); DEFINE_string(package, "", "The package name for the generated class (required)"); @@ -108,6 +110,21 @@ int main(int argc, char** argv) { } const char* const filename = argv[kFileNameParam]; + const bool is_stdout = FLAGS_out == kStdoutFilename; + + std::ofstream outfile; + if (!is_stdout) { + outfile.open(FLAGS_out); + } + + if (FLAGS_apk) { + startop::CompileApkLayouts( + filename, + FLAGS_dex ? startop::CompilationTarget::kDex : startop::CompilationTarget::kJavaLanguage, + is_stdout ? std::cout : outfile); + return 0; + } + const string layout_name = startop::util::FindLayoutNameFromFilename(filename); XMLDocument xml; @@ -119,13 +136,6 @@ int main(int argc, char** argv) { return 1; } - const bool is_stdout = FLAGS_out == kStdoutFilename; - - std::ofstream outfile; - if (!is_stdout) { - outfile.open(FLAGS_out); - } - if (FLAGS_dex) { DexBuilder dex_file; string class_name = StringPrintf("%s.CompiledView", FLAGS_package.c_str()); diff --git a/telecomm/java/android/telecom/CallRedirectionService.java b/telecomm/java/android/telecom/CallRedirectionService.java index b906d0bf3136..329911776993 100644 --- a/telecomm/java/android/telecom/CallRedirectionService.java +++ b/telecomm/java/android/telecom/CallRedirectionService.java @@ -16,6 +16,7 @@ package android.telecom; +import android.annotation.NonNull; import android.annotation.SdkConstant; import android.app.Service; import android.content.Intent; @@ -27,8 +28,8 @@ import android.os.Message; import android.os.RemoteException; import com.android.internal.os.SomeArgs; -import com.android.internal.telecom.ICallRedirectionService; import com.android.internal.telecom.ICallRedirectionAdapter; +import com.android.internal.telecom.ICallRedirectionService; /** * This service can be implemented to interact between Telecom and its implementor @@ -62,22 +63,35 @@ public abstract class CallRedirectionService extends Service { /** * Telecom calls this method to inform the implemented {@link CallRedirectionService} of - * a new outgoing call which is being placed. + * a new outgoing call which is being placed. Telecom does not request to redirect emergency + * calls and does not request to redirect calls with gateway information. + * + * <p>Telecom will cancel the call if Telecom does not receive a response in 5 seconds from + * the implemented {@link CallRedirectionService} set by users. * - * The implemented {@link CallRedirectionService} can call {@link #placeCallUnmodified()}, - * {@link #redirectCall(Uri, PhoneAccountHandle)}, and {@link #cancelCall()} only from here. + * <p>The implemented {@link CallRedirectionService} can call {@link #placeCallUnmodified()}, + * {@link #redirectCall(Uri, PhoneAccountHandle, boolean)}, and {@link #cancelCall()} only + * from here. * - * @param handle the phone number dialed by the user - * @param targetPhoneAccount the {@link PhoneAccountHandle} on which the call will be placed. + * @param handle the phone number dialed by the user, represented in E.164 format if possible + * @param initialPhoneAccount the {@link PhoneAccountHandle} on which the call will be placed. + * @param allowInteractiveResponse a boolean to tell if the implemented + * {@link CallRedirectionService} should allow interactive + * responses with users. Will be {@code false} if, for example + * the device is in car mode and the user would not be able to + * interact with their device. */ - public abstract void onPlaceCall(Uri handle, PhoneAccountHandle targetPhoneAccount); + public abstract void onPlaceCall(@NonNull Uri handle, + @NonNull PhoneAccountHandle initialPhoneAccount, + boolean allowInteractiveResponse); /** * The implemented {@link CallRedirectionService} calls this method to response a request - * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that no changes - * are required to the outgoing call, and that the call should be placed as-is. + * received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that + * no changes are required to the outgoing call, and that the call should be placed as-is. * - * This can only be called from implemented {@link #onPlaceCall(Uri, PhoneAccountHandle)}. + * <p>This can only be called from implemented + * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}. * */ public final void placeCallUnmodified() { @@ -89,29 +103,39 @@ public abstract class CallRedirectionService extends Service { /** * The implemented {@link CallRedirectionService} calls this method to response a request - * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that changes - * are required to the phone number or/and {@link PhoneAccountHandle} for the outgoing call. + * received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that + * changes are required to the phone number or/and {@link PhoneAccountHandle} for the outgoing + * call. Telecom will cancel the call if the implemented {@link CallRedirectionService} + * replies Telecom a handle for an emergency number. * - * This can only be called from implemented {@link #onPlaceCall(Uri, PhoneAccountHandle)}. + * <p>This can only be called from implemented + * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}. * * @param handle the new phone number to dial * @param targetPhoneAccount the {@link PhoneAccountHandle} to use when placing the call. * If {@code null}, no change will be made to the * {@link PhoneAccountHandle} used to place the call. + * @param confirmFirst Telecom will ask users to confirm the redirection via a yes/no dialog + * if the confirmFirst is true, and if the redirection request of this + * response was sent with a true flag of allowInteractiveResponse via + * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} */ - public final void redirectCall(Uri handle, PhoneAccountHandle targetPhoneAccount) { + public final void redirectCall(@NonNull Uri handle, + @NonNull PhoneAccountHandle targetPhoneAccount, + boolean confirmFirst) { try { - mCallRedirectionAdapter.redirectCall(handle, targetPhoneAccount); + mCallRedirectionAdapter.redirectCall(handle, targetPhoneAccount, confirmFirst); } catch (RemoteException e) { } } /** * The implemented {@link CallRedirectionService} calls this method to response a request - * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that an outgoing - * call should be canceled entirely. + * received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that + * an outgoing call should be canceled entirely. * - * This can only be called from implemented {@link #onPlaceCall(Uri, PhoneAccountHandle)}. + * <p>This can only be called from implemented + * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}. * */ public final void cancelCall() { @@ -137,7 +161,8 @@ public abstract class CallRedirectionService extends Service { SomeArgs args = (SomeArgs) msg.obj; try { mCallRedirectionAdapter = (ICallRedirectionAdapter) args.arg1; - onPlaceCall((Uri) args.arg2, (PhoneAccountHandle) args.arg3); + onPlaceCall((Uri) args.arg2, (PhoneAccountHandle) args.arg3, + (boolean) args.arg4); } finally { args.recycle(); } @@ -152,15 +177,20 @@ public abstract class CallRedirectionService extends Service { * Telecom calls this method to inform the CallRedirectionService of a new outgoing call * which is about to be placed. * @param handle the phone number dialed by the user - * @param targetPhoneAccount the URI of the number the user dialed + * @param initialPhoneAccount the URI of the number the user dialed + * @param allowInteractiveResponse a boolean to tell if the implemented + * {@link CallRedirectionService} should allow interactive + * responses with users. */ @Override - public void placeCall(ICallRedirectionAdapter adapter, Uri handle, - PhoneAccountHandle targetPhoneAccount) { + public void placeCall(@NonNull ICallRedirectionAdapter adapter, @NonNull Uri handle, + @NonNull PhoneAccountHandle initialPhoneAccount, + boolean allowInteractiveResponse) { SomeArgs args = SomeArgs.obtain(); args.arg1 = adapter; args.arg2 = handle; - args.arg3 = targetPhoneAccount; + args.arg3 = initialPhoneAccount; + args.arg4 = allowInteractiveResponse; mHandler.obtainMessage(MSG_PLACE_CALL, args).sendToTarget(); } } diff --git a/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl b/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl index 46bf983f52a2..0a42a3f4f02e 100644 --- a/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl +++ b/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl @@ -31,5 +31,6 @@ oneway interface ICallRedirectionAdapter { void placeCallUnmodified(); - void redirectCall(in Uri handle, in PhoneAccountHandle targetPhoneAccount); + void redirectCall(in Uri handle, in PhoneAccountHandle targetPhoneAccount, + boolean confirmFirst); } diff --git a/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl b/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl index d8d360beb64d..c1bc44007b0b 100644 --- a/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl +++ b/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl @@ -30,5 +30,5 @@ import com.android.internal.telecom.ICallRedirectionAdapter; */ oneway interface ICallRedirectionService { void placeCall(in ICallRedirectionAdapter adapter, in Uri handle, - in PhoneAccountHandle targetPhoneAccount); + in PhoneAccountHandle initialPhoneAccount, boolean allowInteractiveResponse); } diff --git a/telephony/java/android/telephony/ims/RcsParticipant.java b/telephony/java/android/telephony/ims/RcsParticipant.java index 70500aaa2e8b..f678ec7e435b 100644 --- a/telephony/java/android/telephony/ims/RcsParticipant.java +++ b/telephony/java/android/telephony/ims/RcsParticipant.java @@ -15,22 +15,111 @@ */ package android.telephony.ims; +import static android.telephony.ims.RcsMessageStore.TAG; + +import android.annotation.NonNull; +import android.annotation.WorkerThread; import android.os.Parcel; import android.os.Parcelable; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.telephony.Rlog; +import android.telephony.ims.aidl.IRcs; +import android.text.TextUtils; + +import com.android.internal.util.Preconditions; /** * RcsParticipant is an RCS capable contact that can participate in {@link RcsThread}s. * @hide - TODO(sahinc) make this public */ public class RcsParticipant implements Parcelable { + // The row ID of this participant in the database + private int mId; + // The phone number of this participant + private String mCanonicalAddress; + // The RCS alias of this participant. This is different than the name of the contact in the + // Contacts app - i.e. RCS protocol allows users to define aliases for themselves that doesn't + // require other users to add them as contacts and give them a name. + private String mAlias; + /** - * Returns the row id of this participant. + * Constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController} + * to create instances of participants. This is not meant to be part of the SDK. + * + * @hide + */ + public RcsParticipant(int id, @NonNull String canonicalAddress) { + mId = id; + mCanonicalAddress = canonicalAddress; + } + + /** + * @return Returns the canonical address (i.e. normalized phone number) for this participant + */ + public String getCanonicalAddress() { + return mCanonicalAddress; + } + + /** + * Sets the canonical address for this participant and updates it in storage. + * @param canonicalAddress the canonical address to update to. + */ + @WorkerThread + public void setCanonicalAddress(@NonNull String canonicalAddress) { + Preconditions.checkNotNull(canonicalAddress); + if (canonicalAddress.equals(mCanonicalAddress)) { + return; + } + + mCanonicalAddress = canonicalAddress; + + try { + IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs")); + if (iRcs != null) { + iRcs.updateRcsParticipantCanonicalAddress(mId, mCanonicalAddress); + } + } catch (RemoteException re) { + Rlog.e(TAG, "RcsParticipant: Exception happened during setCanonicalAddress", re); + } + } + + /** + * @return Returns the alias for this participant. Alias is usually the real name of the person + * themselves. + */ + public String getAlias() { + return mAlias; + } + + /** + * Sets the alias for this participant and persists it in storage. Alias is usually the real + * name of the person themselves. + */ + @WorkerThread + public void setAlias(String alias) { + if (TextUtils.equals(mAlias, alias)) { + return; + } + mAlias = alias; + + try { + IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs")); + if (iRcs != null) { + iRcs.updateRcsParticipantAlias(mId, mAlias); + } + } catch (RemoteException re) { + Rlog.e(TAG, "RcsParticipant: Exception happened during setCanonicalAddress", re); + } + } + + /** + * Returns the row id of this participant. This is not meant to be part of the SDK * - * TODO(sahinc) implement * @hide */ public int getId() { - return 12345; + return mId; } public static final Creator<RcsParticipant> CREATOR = new Creator<RcsParticipant>() { @@ -46,6 +135,9 @@ public class RcsParticipant implements Parcelable { }; protected RcsParticipant(Parcel in) { + mId = in.readInt(); + mCanonicalAddress = in.readString(); + mAlias = in.readString(); } @Override @@ -55,6 +147,8 @@ public class RcsParticipant implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - + dest.writeInt(mId); + dest.writeString(mCanonicalAddress); + dest.writeString(mAlias); } } diff --git a/telephony/java/android/telephony/ims/aidl/IRcs.aidl b/telephony/java/android/telephony/ims/aidl/IRcs.aidl index 9badac5be230..0c958ba719f3 100644 --- a/telephony/java/android/telephony/ims/aidl/IRcs.aidl +++ b/telephony/java/android/telephony/ims/aidl/IRcs.aidl @@ -37,8 +37,13 @@ interface IRcs { Rcs1To1Thread createRcs1To1Thread(in RcsParticipant participant); - RcsParticipant createRcsParticipant(String canonicalAddress); - // RcsThread APIs int getMessageCount(int rcsThreadId); + + // RcsParticipant APIs + RcsParticipant createRcsParticipant(String canonicalAddress); + + void updateRcsParticipantCanonicalAddress(int id, String canonicalAddress); + + void updateRcsParticipantAlias(int id, String alias); }
\ No newline at end of file diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java new file mode 100644 index 000000000000..c402dbffc84b --- /dev/null +++ b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2018 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.tests.ims; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Bundle; +import android.support.test.runner.AndroidJUnit4; +import android.telephony.ims.RcsParticipant; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class RcsParticipantTest { + private static final int ID = 123; + private static final String ALIAS = "alias"; + private static final String CANONICAL_ADDRESS = "+1234567890"; + + @Test + public void testCanUnparcel() { + RcsParticipant rcsParticipant = new RcsParticipant(ID, CANONICAL_ADDRESS); + rcsParticipant.setAlias(ALIAS); + + Bundle bundle = new Bundle(); + bundle.putParcelable("Some key", rcsParticipant); + rcsParticipant = bundle.getParcelable("Some key"); + + assertThat(rcsParticipant.getId()).isEqualTo(ID); + assertThat(rcsParticipant.getAlias()).isEqualTo(ALIAS); + assertThat(rcsParticipant.getCanonicalAddress()).isEqualTo(CANONICAL_ADDRESS); + } +} diff --git a/tests/UsageReportingTest/Android.mk b/tests/UsageReportingTest/Android.mk new file mode 100644 index 000000000000..afb6e16b1fdf --- /dev/null +++ b/tests/UsageReportingTest/Android.mk @@ -0,0 +1,17 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +# Only compile source java files in this apk. +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_USE_AAPT2 := true +LOCAL_STATIC_ANDROID_LIBRARIES := androidx.legacy_legacy-support-v4 + +LOCAL_CERTIFICATE := platform + +LOCAL_PACKAGE_NAME := UsageReportingTest +LOCAL_PRIVATE_PLATFORM_APIS := true + +include $(BUILD_PACKAGE) diff --git a/tests/UsageReportingTest/AndroidManifest.xml b/tests/UsageReportingTest/AndroidManifest.xml new file mode 100644 index 000000000000..be0b09e972a5 --- /dev/null +++ b/tests/UsageReportingTest/AndroidManifest.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + Note: Add android:sharedUserId="android.uid.system" to the root element to simulate the system UID + caller case. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.tests.usagereporter" + > + + <application android:label="@string/reporter_app"> + <activity android:name="UsageReporterActivity" + android:label="UsageReporter"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + </application> +</manifest> diff --git a/tests/UsageReportingTest/res/layout/row_item.xml b/tests/UsageReportingTest/res/layout/row_item.xml new file mode 100644 index 000000000000..1eb2dab29124 --- /dev/null +++ b/tests/UsageReportingTest/res/layout/row_item.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingLeft="16dp" + android:paddingRight="16dp" + android:background="@color/inactive_color"> + + <TextView android:id="@+id/token" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:textStyle="bold"/> + + <Button android:id="@+id/start" style="@style/ActionButton" + android:text="@string/start" /> + + <Button android:id="@+id/stop" style="@style/ActionButton" + android:text="@string/stop" /> +</LinearLayout> diff --git a/tests/UsageReportingTest/res/menu/main.xml b/tests/UsageReportingTest/res/menu/main.xml new file mode 100644 index 000000000000..9847c2dce8f2 --- /dev/null +++ b/tests/UsageReportingTest/res/menu/main.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!-- Copyright (C) 2018 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. +--> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/add_token" + android:title="@string/add_token"/> + <item android:id="@+id/add_many_tokens" + android:title="@string/add_many_tokens"/> + <item android:id="@+id/stop_all" + android:title="@string/stop_all_tokens"/> + <group android:checkableBehavior="all"> + <item android:id="@+id/restore_on_start" + android:title="@string/restore_tokens_on_start"/> + </group> +</menu>
\ No newline at end of file diff --git a/tests/UsageReportingTest/res/values/colors.xml b/tests/UsageReportingTest/res/values/colors.xml new file mode 100644 index 000000000000..03bcf8a60182 --- /dev/null +++ b/tests/UsageReportingTest/res/values/colors.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <color name="active_color">#FFF</color> + <color name="inactive_color">#AAA</color> +</resources>
\ No newline at end of file diff --git a/tests/UsageReportingTest/res/values/strings.xml b/tests/UsageReportingTest/res/values/strings.xml new file mode 100644 index 000000000000..015290e732a0 --- /dev/null +++ b/tests/UsageReportingTest/res/values/strings.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <!-- Do not translate --> + <string name="reporter_app">Usage Reporter App</string> + <!-- Do not translate --> + <string name="start">Start</string> + <!-- Do not translate --> + <string name="stop">Stop</string> + <!-- Do not translate --> + <string name="default_token">SuperSecretToken</string> + + <!-- Do not translate --> + <string name="add_token">Add Token</string> + <!-- Do not translate --> + <string name="add_many_tokens">Add Many Tokens</string> + <!-- Do not translate --> + <string name="stop_all_tokens">Stop All</string> + <!-- Do not translate --> + <string name="restore_tokens_on_start">Readd Tokens on Start</string> + + + <!-- Do not translate --> + <string name="token_query">Enter token(s) (delimit tokens with commas)</string> + <!-- Do not translate --> + <string name="many_tokens_query">Generate how many tokens?</string> + <!-- Do not translate --> + <string name="stop_all_tokens_query">Stop all tokens?</string> + <!-- Do not translate --> + <string name="ok">OK</string> + <!-- Do not translate --> + <string name="cancel">Cancel</string> +</resources>
\ No newline at end of file diff --git a/tests/UsageReportingTest/res/values/styles.xml b/tests/UsageReportingTest/res/values/styles.xml new file mode 100644 index 000000000000..e5b86c5e836b --- /dev/null +++ b/tests/UsageReportingTest/res/values/styles.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <style name="ActionButton"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:textAppearance">@style/TextAppearance.ActionButton</item> + </style> + + <style name="TextAppearance" parent="android:TextAppearance"> + </style> + + <style name="TextAppearance.ActionButton"> + <item name="android:textStyle">italic</item> + </style> + +</resources> diff --git a/tests/UsageReportingTest/src/com/android/tests/usagereporter/UsageReporterActivity.java b/tests/UsageReportingTest/src/com/android/tests/usagereporter/UsageReporterActivity.java new file mode 100644 index 000000000000..946be8fe93d3 --- /dev/null +++ b/tests/UsageReportingTest/src/com/android/tests/usagereporter/UsageReporterActivity.java @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2018 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.tests.usagereporter; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.ListActivity; +import android.app.usage.UsageStatsManager; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.text.InputType; +import android.text.TextUtils; +import android.util.ArraySet; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import java.util.ArrayList; + +public class UsageReporterActivity extends ListActivity { + + private Activity mActivity; + private final ArrayList<String> mTokens = new ArrayList(); + private final ArraySet<String> mActives = new ArraySet(); + private UsageStatsManager mUsageStatsManager; + private Adapter mAdapter; + private boolean mRestoreOnStart = false; + private static Context sContext; + + /** Called with the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + sContext = getApplicationContext(); + + mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE); + + mAdapter = new Adapter(); + setListAdapter(mAdapter); + } + + @Override + protected void onStart() { + super.onStart(); + mActivity = this; + + + if (mRestoreOnStart) { + ArrayList<String> removed = null; + for (String token : mActives) { + try { + mUsageStatsManager.reportUsageStart(mActivity, token); + } catch (Exception e) { + // Somthing went wrong, recover and move on + if (removed == null) { + removed = new ArrayList(); + } + removed.add(token); + } + } + if (removed != null) { + for (String token : removed) { + mActives.remove(token); + } + } + } else { + mActives.clear(); + } + } + + /** + * Called when the activity is about to start interacting with the user. + */ + @Override + protected void onResume() { + super.onResume(); + mAdapter.notifyDataSetChanged(); + } + + + /** + * Called when your activity's options menu needs to be created. + */ + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.main, menu); + return super.onCreateOptionsMenu(menu); + } + + + /** + * Called when a menu item is selected. + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.add_token: + callAddToken(); + return true; + case R.id.add_many_tokens: + callAddManyTokens(); + return true; + case R.id.stop_all: + callStopAll(); + return true; + case R.id.restore_on_start: + mRestoreOnStart = !mRestoreOnStart; + item.setChecked(mRestoreOnStart); + return true; + } + + return super.onOptionsItemSelected(item); + } + + private void callAddToken() { + final AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(getString(R.string.token_query)); + final EditText input = new EditText(this); + input.setInputType(InputType.TYPE_CLASS_TEXT); + input.setHint(getString(R.string.default_token)); + builder.setView(input); + + builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + String tokenNames = input.getText().toString().trim(); + if (TextUtils.isEmpty(tokenNames)) { + tokenNames = getString(R.string.default_token); + } + String[] tokens = tokenNames.split(","); + for (String token : tokens) { + if (mTokens.contains(token)) continue; + mTokens.add(token); + } + mAdapter.notifyDataSetChanged(); + + } + }); + builder.setNegativeButton(getString(R.string.cancel), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + } + }); + + builder.show(); + } + + private void callAddManyTokens() { + final AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(getString(R.string.many_tokens_query)); + final EditText input = new EditText(this); + input.setInputType(InputType.TYPE_CLASS_NUMBER); + builder.setView(input); + + builder.setPositiveButton(getString(R.string.ok), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + String val = input.getText().toString().trim(); + if (TextUtils.isEmpty(val)) return; + int n = Integer.parseInt(val); + for (int i = 0; i < n; i++) { + final String token = getString(R.string.default_token) + i; + if (mTokens.contains(token)) continue; + mTokens.add(token); + } + mAdapter.notifyDataSetChanged(); + + } + }); + builder.setNegativeButton(getString(R.string.cancel), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + } + }); + + builder.show(); + } + + private void callStopAll() { + final AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(getString(R.string.stop_all_tokens_query)); + + builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + for (String token : mActives) { + mUsageStatsManager.reportUsageStop(mActivity, token); + } + mActives.clear(); + mAdapter.notifyDataSetChanged(); + } + }); + builder.setNegativeButton(getString(R.string.cancel), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + } + }); + builder.show(); + } + + /** + * A call-back for when the user presses the back button. + */ + OnClickListener mStartListener = new OnClickListener() { + public void onClick(View v) { + final View parent = (View) v.getParent(); + final String token = ((TextView) parent.findViewById(R.id.token)).getText().toString(); + try { + mUsageStatsManager.reportUsageStart(mActivity, token); + } catch (Exception e) { + Toast.makeText(sContext, e.toString(), Toast.LENGTH_LONG).show(); + } + parent.setBackgroundColor(getColor(R.color.active_color)); + mActives.add(token); + } + }; + + /** + * A call-back for when the user presses the clear button. + */ + OnClickListener mStopListener = new OnClickListener() { + public void onClick(View v) { + final View parent = (View) v.getParent(); + + final String token = ((TextView) parent.findViewById(R.id.token)).getText().toString(); + try { + mUsageStatsManager.reportUsageStop(mActivity, token); + } catch (Exception e) { + Toast.makeText(sContext, e.toString(), Toast.LENGTH_LONG).show(); + } + parent.setBackgroundColor(getColor(R.color.inactive_color)); + mActives.remove(token); + } + }; + + + private class Adapter extends BaseAdapter { + @Override + public int getCount() { + return mTokens.size(); + } + + @Override + public Object getItem(int position) { + return mTokens.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + final ViewHolder holder; + if (convertView == null) { + convertView = LayoutInflater.from(UsageReporterActivity.this) + .inflate(R.layout.row_item, parent, false); + holder = new ViewHolder(); + holder.tokenName = (TextView) convertView.findViewById(R.id.token); + + holder.startButton = ((Button) convertView.findViewById(R.id.start)); + holder.startButton.setOnClickListener(mStartListener); + holder.stopButton = ((Button) convertView.findViewById(R.id.stop)); + holder.stopButton.setOnClickListener(mStopListener); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } + + final String token = mTokens.get(position); + holder.tokenName.setText(mTokens.get(position)); + if (mActives.contains(token)) { + convertView.setBackgroundColor(getColor(R.color.active_color)); + } else { + convertView.setBackgroundColor(getColor(R.color.inactive_color)); + } + return convertView; + } + } + + private static class ViewHolder { + public TextView tokenName; + public Button startButton; + public Button stopButton; + } +} diff --git a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java index 3d8ce21a2c00..3c628f6e0013 100644 --- a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java +++ b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java @@ -155,7 +155,7 @@ public class UsageStatsActivity extends ListActivity { intent.setPackage(getPackageName()); intent.putExtra(EXTRA_KEY_TIMEOUT, true); mUsageStatsManager.registerAppUsageObserver(1, packages, - 30, TimeUnit.SECONDS, PendingIntent.getActivity(UsageStatsActivity.this, + 60, TimeUnit.SECONDS, PendingIntent.getActivity(UsageStatsActivity.this, 1, intent, 0)); } } diff --git a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java index d5987a5373b4..85646987940f 100644 --- a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java +++ b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java @@ -28,6 +28,7 @@ import android.view.DisplayCutout; import android.view.IWindowSession; import android.view.InsetsState; import android.view.Surface; +import android.view.SurfaceControl; import android.view.View; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; @@ -106,7 +107,7 @@ public class MainActivity extends Activity { window.mSeq, mLayoutParams, -1, -1, View.VISIBLE, 0, -1, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, new DisplayCutout.ParcelableWrapper(), new MergedConfiguration(), - new Surface(), new InsetsState()); + new SurfaceControl(), new InsetsState()); } catch (RemoteException e) { e.printStackTrace(); } diff --git a/tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java b/tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java index 7f8e7b5456c2..ba0448c98387 100644 --- a/tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java +++ b/tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java @@ -35,8 +35,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.net.IpPrefix; import android.net.MacAddress; -import android.net.util.SharedLog; import android.net.dhcp.DhcpServer.Clock; +import android.net.util.SharedLog; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -126,7 +126,7 @@ public class DhcpLeaseRepositoryTest { mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS); // /28 should have 16 addresses, 14 w/o the first/last, 11 w/o excluded addresses - requestAddresses((byte)11); + requestAddresses((byte) 11); try { mRepo.getOffer(null, TEST_MAC_2, diff --git a/tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java b/tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java new file mode 100644 index 000000000000..4a6f20a043bb --- /dev/null +++ b/tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2018 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.net.dhcp; + +import static android.net.InetAddresses.parseNumericAddress; + +import static com.google.android.collect.Sets.newHashSet; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.net.LinkAddress; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.Inet4Address; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class DhcpServingParamsParcelExtTest { + private static final Inet4Address TEST_ADDRESS = inet4Addr("192.168.0.123"); + private static final int TEST_ADDRESS_PARCELED = 0xc0a8007b; + private static final int TEST_PREFIX_LENGTH = 17; + private static final int TEST_LEASE_TIME_SECS = 120; + private static final int TEST_MTU = 1000; + private static final Set<Inet4Address> TEST_ADDRESS_SET = + newHashSet(inet4Addr("192.168.1.123"), inet4Addr("192.168.1.124")); + private static final Set<Integer> TEST_ADDRESS_SET_PARCELED = + newHashSet(0xc0a8017b, 0xc0a8017c); + + private DhcpServingParamsParcelExt mParcel; + + @Before + public void setUp() { + mParcel = new DhcpServingParamsParcelExt(); + } + + @Test + public void testSetServerAddr() { + mParcel.setServerAddr(new LinkAddress(TEST_ADDRESS, TEST_PREFIX_LENGTH)); + + assertEquals(TEST_ADDRESS_PARCELED, mParcel.serverAddr); + assertEquals(TEST_PREFIX_LENGTH, mParcel.serverAddrPrefixLength); + } + + @Test + public void testSetDefaultRouters() { + mParcel.setDefaultRouters(TEST_ADDRESS_SET); + assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.defaultRouters)); + } + + @Test + public void testSetDnsServers() { + mParcel.setDnsServers(TEST_ADDRESS_SET); + assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.dnsServers)); + } + + @Test + public void testSetExcludedAddrs() { + mParcel.setExcludedAddrs(TEST_ADDRESS_SET); + assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.excludedAddrs)); + } + + @Test + public void testSetDhcpLeaseTimeSecs() { + mParcel.setDhcpLeaseTimeSecs(TEST_LEASE_TIME_SECS); + assertEquals(TEST_LEASE_TIME_SECS, mParcel.dhcpLeaseTimeSecs); + } + + @Test + public void testSetLinkMtu() { + mParcel.setLinkMtu(TEST_MTU); + assertEquals(TEST_MTU, mParcel.linkMtu); + } + + @Test + public void testSetMetered() { + mParcel.setMetered(true); + assertTrue(mParcel.metered); + mParcel.setMetered(false); + assertFalse(mParcel.metered); + } + + private static Inet4Address inet4Addr(String addr) { + return (Inet4Address) parseNumericAddress(addr); + } + + private static Set<Integer> asSet(int[] ints) { + return IntStream.of(ints).boxed().collect(Collectors.toSet()); + } +} diff --git a/tests/net/java/android/net/dhcp/DhcpServingParamsTest.java b/tests/net/java/android/net/dhcp/DhcpServingParamsTest.java index b6a4073a64da..2ab224667b8a 100644 --- a/tests/net/java/android/net/dhcp/DhcpServingParamsTest.java +++ b/tests/net/java/android/net/dhcp/DhcpServingParamsTest.java @@ -16,6 +16,7 @@ package android.net.dhcp; +import static android.net.NetworkUtils.inet4AddressToIntHTH; import static android.net.dhcp.DhcpServingParams.MTU_UNSET; import static junit.framework.Assert.assertEquals; @@ -27,6 +28,7 @@ import static java.net.InetAddress.parseNumericAddress; import android.annotation.NonNull; import android.annotation.Nullable; import android.net.LinkAddress; +import android.net.NetworkUtils; import android.net.dhcp.DhcpServingParams.InvalidParameterException; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -35,8 +37,10 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.lang.reflect.Modifier; import java.net.Inet4Address; import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -56,6 +60,7 @@ public class DhcpServingParamsTest { private static final int TEST_MTU = 1500; private static final Set<Inet4Address> TEST_EXCLUDED_ADDRS = new HashSet<>( Arrays.asList(parseAddr("192.168.0.200"), parseAddr("192.168.0.201"))); + private static final boolean TEST_METERED = true; @Before public void setUp() { @@ -65,7 +70,8 @@ public class DhcpServingParamsTest { .setDnsServers(TEST_DNS_SERVERS) .setServerAddr(TEST_LINKADDR) .setLinkMtu(TEST_MTU) - .setExcludedAddrs(TEST_EXCLUDED_ADDRS); + .setExcludedAddrs(TEST_EXCLUDED_ADDRS) + .setMetered(TEST_METERED); } @Test @@ -91,6 +97,7 @@ public class DhcpServingParamsTest { assertEquals(TEST_DNS_SERVERS, params.dnsServers); assertEquals(TEST_LINKADDR, params.serverAddr); assertEquals(TEST_MTU, params.linkMtu); + assertEquals(TEST_METERED, params.metered); assertContains(params.excludedAddrs, TEST_EXCLUDED_ADDRS); assertContains(params.excludedAddrs, TEST_DEFAULT_ROUTERS); @@ -159,6 +166,39 @@ public class DhcpServingParamsTest { mBuilder.setDefaultRouters(parseAddr("192.168.254.254")).build(); } + @Test + public void testFromParcelableObject() throws InvalidParameterException { + final DhcpServingParams params = mBuilder.build(); + final DhcpServingParamsParcel parcel = new DhcpServingParamsParcel(); + parcel.defaultRouters = toIntArray(TEST_DEFAULT_ROUTERS); + parcel.dhcpLeaseTimeSecs = TEST_LEASE_TIME_SECS; + parcel.dnsServers = toIntArray(TEST_DNS_SERVERS); + parcel.serverAddr = inet4AddressToIntHTH(TEST_SERVER_ADDR); + parcel.serverAddrPrefixLength = TEST_LINKADDR.getPrefixLength(); + parcel.linkMtu = TEST_MTU; + parcel.excludedAddrs = toIntArray(TEST_EXCLUDED_ADDRS); + parcel.metered = TEST_METERED; + final DhcpServingParams parceled = DhcpServingParams.fromParcelableObject(parcel); + + assertEquals(params.defaultRouters, parceled.defaultRouters); + assertEquals(params.dhcpLeaseTimeSecs, parceled.dhcpLeaseTimeSecs); + assertEquals(params.dnsServers, parceled.dnsServers); + assertEquals(params.serverAddr, parceled.serverAddr); + assertEquals(params.linkMtu, parceled.linkMtu); + assertEquals(params.excludedAddrs, parceled.excludedAddrs); + assertEquals(params.metered, parceled.metered); + + // Ensure that we do not miss any field if added in the future + final long numFields = Arrays.stream(DhcpServingParams.class.getDeclaredFields()) + .filter(f -> !Modifier.isStatic(f.getModifiers())) + .count(); + assertEquals(7, numFields); + } + + private static int[] toIntArray(Collection<Inet4Address> addrs) { + return addrs.stream().mapToInt(NetworkUtils::inet4AddressToIntHTH).toArray(); + } + private static <T> void assertContains(@NonNull Set<T> set, @NonNull Set<T> subset) { for (final T elem : subset) { assertContains(set, elem); diff --git a/tests/net/java/android/net/ip/IpServerTest.java b/tests/net/java/android/net/ip/IpServerTest.java index 2c675c68a076..017822896610 100644 --- a/tests/net/java/android/net/ip/IpServerTest.java +++ b/tests/net/java/android/net/ip/IpServerTest.java @@ -16,6 +16,16 @@ package android.net.ip; +import static android.net.ConnectivityManager.TETHERING_BLUETOOTH; +import static android.net.ConnectivityManager.TETHERING_USB; +import static android.net.ConnectivityManager.TETHERING_WIFI; +import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR; +import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; +import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR; +import static android.net.ip.IpServer.STATE_AVAILABLE; +import static android.net.ip.IpServer.STATE_TETHERED; +import static android.net.ip.IpServer.STATE_UNAVAILABLE; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -31,16 +41,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR; -import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; -import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR; -import static android.net.ConnectivityManager.TETHERING_BLUETOOTH; -import static android.net.ConnectivityManager.TETHERING_USB; -import static android.net.ConnectivityManager.TETHERING_WIFI; -import static android.net.ip.IpServer.STATE_AVAILABLE; -import static android.net.ip.IpServer.STATE_TETHERED; -import static android.net.ip.IpServer.STATE_UNAVAILABLE; - import android.net.INetworkStatsService; import android.net.InterfaceConfiguration; import android.net.IpPrefix; @@ -60,8 +60,6 @@ import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.text.TextUtils; -import java.net.Inet4Address; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -71,6 +69,8 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.net.Inet4Address; + @RunWith(AndroidJUnit4.class) @SmallTest public class IpServerTest { diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 7b2f07d5cb68..fef0ed711b66 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -64,7 +64,7 @@ interface IWifiManager Map getAllMatchingFqdnsForScanResults(in List<ScanResult> scanResult); - List<OsuProvider> getMatchingOsuProviders(in List<ScanResult> scanResult); + Map getMatchingOsuProviders(in List<ScanResult> scanResult); Map getMatchingPasspointConfigsForOsuProviders(in List<OsuProvider> osuProviders); diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java index 7cdd16a6a4ed..af5ad512a424 100644 --- a/wifi/java/android/net/wifi/WifiInfo.java +++ b/wifi/java/android/net/wifi/WifiInfo.java @@ -114,6 +114,11 @@ public class WifiInfo implements Parcelable { private boolean mTrusted; /** + * OSU (Online Sign Up) AP for Passpoint R2. + */ + private boolean mOsuAp; + + /** * Running total count of lost (not ACKed) transmitted unicast data packets. * @hide */ @@ -190,6 +195,7 @@ public class WifiInfo implements Parcelable { setFrequency(-1); setMeteredHint(false); setEphemeral(false); + setOsuAp(false); txBad = 0; txSuccess = 0; rxSuccess = 0; @@ -219,6 +225,7 @@ public class WifiInfo implements Parcelable { mMeteredHint = source.mMeteredHint; mEphemeral = source.mEphemeral; mTrusted = source.mTrusted; + mOsuAp = source.mOsuAp; txBad = source.txBad; txRetries = source.txRetries; txSuccess = source.txSuccess; @@ -411,6 +418,15 @@ public class WifiInfo implements Parcelable { return mTrusted; } + /** {@hide} */ + public void setOsuAp(boolean osuAp) { + mOsuAp = osuAp; + } + + /** {@hide} */ + public boolean isOsuAp() { + return mOsuAp; + } /** @hide */ @UnsupportedAppUsage @@ -565,6 +581,7 @@ public class WifiInfo implements Parcelable { dest.writeLong(rxSuccess); dest.writeDouble(rxSuccessRate); mSupplicantState.writeToParcel(dest, flags); + dest.writeInt(mOsuAp ? 1 : 0); } /** Implement the Parcelable interface {@hide} */ @@ -600,6 +617,7 @@ public class WifiInfo implements Parcelable { info.rxSuccess = in.readLong(); info.rxSuccessRate = in.readDouble(); info.mSupplicantState = SupplicantState.CREATOR.createFromParcel(in); + info.mOsuAp = in.readInt() != 0; return info; } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index f2a3b42e1668..ad2ed81398c1 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -1234,12 +1234,13 @@ public class WifiManager { * An empty list will be returned if no match is found. * * @param scanResults a list of ScanResult - * @return A list of {@link OsuProvider} that does not contain duplicate entries. + * @return Map that consists {@link OsuProvider} and a list of matching {@link ScanResult} * @throws UnsupportedOperationException if Passpoint is not enabled on the device. * @hide */ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) - public List<OsuProvider> getMatchingOsuProviders(List<ScanResult> scanResults) { + public Map<OsuProvider, List<ScanResult>> getMatchingOsuProviders( + List<ScanResult> scanResults) { try { return mService.getMatchingOsuProviders(scanResults); } catch (RemoteException e) { diff --git a/wifi/java/com/android/server/wifi/AbstractWifiService.java b/wifi/java/com/android/server/wifi/AbstractWifiService.java index 23961080110e..3d328a117241 100644 --- a/wifi/java/com/android/server/wifi/AbstractWifiService.java +++ b/wifi/java/com/android/server/wifi/AbstractWifiService.java @@ -21,6 +21,7 @@ package com.android.server.wifi; import android.content.pm.ParceledListSlice; import android.net.DhcpInfo; import android.net.Network; +import android.net.wifi.IDppCallback; import android.net.wifi.INetworkRequestMatchCallback; import android.net.wifi.ISoftApCallback; import android.net.wifi.ITrafficStateCallback; @@ -35,6 +36,7 @@ import android.net.wifi.hotspot2.OsuProvider; import android.net.wifi.hotspot2.PasspointConfiguration; import android.os.IBinder; import android.os.Messenger; +import android.os.RemoteException; import android.os.ResultReceiver; import android.os.WorkSource; @@ -124,7 +126,8 @@ public abstract class AbstractWifiService extends IWifiManager.Stub { } @Override - public List<OsuProvider> getMatchingOsuProviders(List<ScanResult> scanResults) { + public Map<OsuProvider, List<ScanResult>> getMatchingOsuProviders( + List<ScanResult> scanResults) { throw new UnsupportedOperationException(); } @@ -470,4 +473,26 @@ public abstract class AbstractWifiService extends IWifiManager.Stub { public String[] getFactoryMacAddresses() { throw new UnsupportedOperationException(); } + + @Override + public void setDeviceMobilityState(int state) { + throw new UnsupportedOperationException(); + } + + @Override + public void startDppAsConfiguratorInitiator(IBinder binder, String enrolleeUri, + int selectedNetworkId, int netRole, IDppCallback callback) { + throw new UnsupportedOperationException(); + } + + @Override + public void startDppAsEnrolleeInitiator(IBinder binder, String configuratorUri, + IDppCallback callback) { + throw new UnsupportedOperationException(); + } + + @Override + public void stopDppSession() throws RemoteException { + throw new UnsupportedOperationException(); + } } diff --git a/wifi/tests/src/android/net/wifi/WifiInfoTest.java b/wifi/tests/src/android/net/wifi/WifiInfoTest.java index fb0af5fbcce3..677bf371c781 100644 --- a/wifi/tests/src/android/net/wifi/WifiInfoTest.java +++ b/wifi/tests/src/android/net/wifi/WifiInfoTest.java @@ -47,6 +47,7 @@ public class WifiInfoTest { writeWifiInfo.txBad = TEST_TX_BAD; writeWifiInfo.rxSuccess = TEST_RX_SUCCESS; writeWifiInfo.setTrusted(true); + writeWifiInfo.setOsuAp(true); Parcel parcel = Parcel.obtain(); writeWifiInfo.writeToParcel(parcel, 0); @@ -60,5 +61,6 @@ public class WifiInfoTest { assertEquals(TEST_TX_BAD, readWifiInfo.txBad); assertEquals(TEST_RX_SUCCESS, readWifiInfo.rxSuccess); assertTrue(readWifiInfo.isTrusted()); + assertTrue(readWifiInfo.isOsuAp()); } } |