Merge "Fix restoring animator_duration_scale back to default"
diff --git a/Android.bp b/Android.bp
index eb9cbbb..93c94e3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -872,6 +872,7 @@
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/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java
index 74edffb..b905e94 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 @@
* 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 @@
put("joystick", InputDevice.SOURCE_JOYSTICK);
}};
+ private static final Map<String, InputCmd> COMMANDS = new HashMap<String, InputCmd>();
/**
* Command-line entry point.
@@ -60,217 +68,256 @@
(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];
- }
- 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;
+ // Get source (optional).
+ if (SOURCES.containsKey(arg)) {
+ inputSource = SOURCES.get(arg);
+ arg = nextArgRequired();
+ }
+
+ // 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);
+ }
+ }
+
+ throw new IllegalArgumentException("Error: Unknown command: " + arg);
+ }
+
+ 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;
}
- } 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;
+ 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);
+ }
+
+ /**
+ * 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);
}
}
- } 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;
+ if (buff.charAt(i) == '%') {
+ escapeFlag = true;
}
- } 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();
- return;
}
- } catch (NumberFormatException ex) {
+
+ 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);
+ }
}
- System.err.println(INVALID_ARGUMENTS + command);
- showUsage();
+ }
+
+ 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);
+ }
+
+ 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);
+
+ injectKeyEvent(event);
+ if (longpress) {
+ repeatCount++;
+ injectKeyEvent(KeyEvent.changeTimeRepeat(event, now, repeatCount,
+ KeyEvent.FLAG_LONG_PRESS));
+ }
+ injectKeyEvent(KeyEvent.changeAction(event, KeyEvent.ACTION_UP));
+ }
+ }
+
+ 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);
+ }
+ }
+
+ 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);
+ }
+ }
+
+ class InputSwipe implements InputCmd {
+ @Override
+ public void run(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+ sendSwipe(inputSource, displayId, false);
+ }
+
+ 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);
+ }
+ }
+
+ class InputDragAndDrop extends InputSwipe {
+ @Override
+ public void run(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+ sendSwipe(inputSource, displayId, true);
+ }
+ }
+
+ 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);
+ }
+
+ /**
+ * 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);
+ }
}
/**
- * 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.
+ * Abstract class for command
+ * use nextArgRequired or nextArg to check next argument if necessary.
*/
- private void sendText(int source, String text) {
-
- 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;
- }
- }
-
- char[] chars = buff.toString().toCharArray();
-
- 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(e);
- }
+ private interface InputCmd {
+ void run(int inputSource, int displayId);
}
- 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));
- }
- 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);
- }
-
- private void sendSwipe(int inputSource, float x1, float y1, float x2, float y2, int duration) {
- if (duration < 0) {
- duration = 300;
- }
- 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();
- }
- 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;
- }
- 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);
- }
- 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();
- }
- 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
- */
- private void sendMove(int inputSource, float dx, float dy) {
- long now = SystemClock.uptimeMillis();
- injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, dx, dy, 0.0f);
- }
-
- 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 @@
*
* @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 @@
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/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 3a2038d..666f721 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -142,19 +142,20 @@
*
* @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 27ae0b0..a929fe0 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -307,13 +307,14 @@
* @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 @@
*
* @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 @@
}
}
- 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 @@
@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 void removeOnColorsChangedListener(@NonNull OnColorsChangedListener callback,
int userId) {
- sGlobals.removeOnColorsChangedListener(callback, userId);
+ sGlobals.removeOnColorsChangedListener(callback, userId, mContext.getDisplayId());
}
/**
@@ -947,7 +949,7 @@
*/
@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/net/dhcp/DhcpServingParamsParcel.aidl b/core/java/android/net/dhcp/DhcpServingParamsParcel.aidl
new file mode 100644
index 0000000..7b8b9ee
--- /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/provider/Settings.java b/core/java/android/provider/Settings.java
index bb1784a..940736a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9415,7 +9415,8 @@
"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 +9425,15 @@
"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/wallpaper/IWallpaperConnection.aidl b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
index a976d0e..f334d9d 100644
--- a/core/java/android/service/wallpaper/IWallpaperConnection.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
@@ -27,5 +27,5 @@
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 9079502..984846e 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -645,7 +645,7 @@
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.");
@@ -1473,7 +1473,7 @@
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/res/res/drawable/ic_account_circle.xml b/core/res/res/drawable/ic_account_circle.xml
index f7317db..71691ad 100644
--- a/core/res/res/drawable/ic_account_circle.xml
+++ b/core/res/res/drawable/ic_account_circle.xml
@@ -14,18 +14,14 @@
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 0000000..bd40f4d
--- /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 1dad977..5ab5045 100644
--- a/core/res/res/drawable/ic_corp_badge.xml
+++ b/core/res/res/drawable/ic_corp_badge.xml
@@ -15,22 +15,11 @@
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/config.xml b/core/res/res/values/config.xml
index e4abf8f..8735557 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 a7bc57a..0878562 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 e8cbf66..c2d97bc 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/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index ac57d20..68b08f9 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -269,6 +269,7 @@
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/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index 3d50d2d..29e4256 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -139,6 +139,7 @@
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 @@
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 1d3d607..5af7d43 100644
--- a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
@@ -31,7 +31,7 @@
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 @@
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 ad11a1d..5107660 100644
--- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
@@ -26,7 +26,7 @@
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 a64e844..286f5f1 100644
--- a/libs/hwui/tests/common/scenes/TvApp.cpp
+++ b/libs/hwui/tests/common/scenes/TvApp.cpp
@@ -48,7 +48,7 @@
class TvApp : public TestScene {
public:
- TvApp(BitmapAllocationTestUtils::BitmapAllocator allocator)
+ explicit TvApp(BitmapAllocationTestUtils::BitmapAllocator allocator)
: TestScene(), mAllocator(allocator) {}
sp<RenderNode> mBg;
@@ -232,7 +232,7 @@
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 @@
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 @@
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 479c462..635429d 100644
--- a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
+++ b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
@@ -44,7 +44,7 @@
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 1168ff2..817c1f3 100644
--- a/libs/hwui/tests/unit/ThreadBaseTests.cpp
+++ b/libs/hwui/tests/unit/ThreadBaseTests.cpp
@@ -95,7 +95,7 @@
};
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 @@
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/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 181ee53..acbc828 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -1154,7 +1154,7 @@
private boolean mRegistered = false;
public MessageHandler(Looper looper, MediaController.Callback cb) {
- super(looper, null, true);
+ super(looper);
mCallback = cb;
}
@@ -1193,6 +1193,7 @@
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 8962bb7..df8cc35 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -1453,7 +1453,7 @@
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 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/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 6a0e8ad..c4c8bc7 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 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 @@
*/
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
@Override
public void onMetadataChanged(MediaMetadata metadata) {
+ final boolean notify;
synchronized (this) {
+ boolean neededMedia = needsMediaLocked();
mMediaMetaData = metadata;
+ notify = neededMedia != needsMediaLocked();
}
- notifyChange();
+ if (notify) {
+ 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/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index d3aa5ab..6d78f72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -632,7 +632,7 @@
mBubbleController.setExpandListener(mBubbleExpandListener);
KeyguardSliceProvider sliceProvider = KeyguardSliceProvider.getAttachedInstance();
if (sliceProvider != null) {
- sliceProvider.initDependencies();
+ sliceProvider.initDependencies(mMediaManager, mStatusBarStateController);
} else {
Log.w(TAG, "Cannot init KeyguardSliceProvider dependencies");
}
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 a8ff0b2..cfc19ef 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.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 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 @@
private AlarmManager mAlarmManager;
@Mock
private NotificationMediaManager mNotificationMediaManager;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
private TestableKeyguardSliceProvider mProvider;
private boolean mIsZenMode;
@@ -76,7 +80,7 @@
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 @@
@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 @@
@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 @@
protected String getFormattedDateLocked() {
return super.getFormattedDateLocked() + mCounter++;
}
-
- @Override
- public void initDependencies() {
- mMediaManager = mNotificationMediaManager;
- }
}
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 2346cfc..5cea56e 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.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.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.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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 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 @@
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()");
+ runInternal(this::initialize);
+ }
- // fetch package manager
- mPackageManager = mContext.getPackageManager();
+ private void initialize() {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- // fetch power manager
- mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ 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);
- // fetch activity manager
- 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);
- // prepare worker thread
- mLocationHandler = new LocationWorkerHandler(BackgroundThread.get().getLooper());
+ mAppOps.startWatchingMode(
+ AppOpsManager.OP_COARSE_LOCATION,
+ null,
+ AppOpsManager.WATCH_FOREGROUND_CHANGES,
+ new AppOpsManager.OnOpChangedInternalListener() {
+ public void onOpChanged(int op, String packageName) {
+ mHandler.post(() -> onAppOpChanged());
+ }
+ });
- // prepare mLocationHandler's dependents
- mLocationFudger = new LocationFudger(mContext, mLocationHandler);
- mBlacklist = new LocationBlacklist(mContext, mLocationHandler);
- mBlacklist.init();
- mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
+ mPackageManager.addOnPermissionsChangeListener(
+ uid -> runInternal(this::onPermissionsChanged));
- // 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);
+ mActivityManager.addOnUidImportanceListener(
+ (uid, importance) -> mHandler.post(
+ () -> onUidImportanceChanged(uid, importance)),
+ FOREGROUND_IMPORTANCE_CUTOFF);
- PackageManager.OnPermissionsChangedListener permissionListener = uid -> {
- synchronized (mLock) {
- applyAllProviderRequirementsLocked();
- }
- };
- mPackageManager.addOnPermissionsChangeListener(permissionListener);
+ mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ updateUserProfiles(mCurrentUserId);
- // listen for background/foreground changes
- ActivityManager.OnUidImportanceListener uidImportanceListener =
- (uid, importance) -> mLocationHandler.post(
- () -> onUidImportanceChanged(uid, importance));
- mActivityManager.addOnUidImportanceListener(uidImportanceListener,
- FOREGROUND_IMPORTANCE_CUTOFF);
+ updateBackgroundThrottlingWhitelist();
- mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- updateUserProfiles(mCurrentUserId);
-
- updateBackgroundThrottlingWhitelistLocked();
- updateLastLocationMaxAgeLocked();
-
- // 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 @@
updateUserProfiles(mCurrentUserId);
}
}
- }, UserHandle.ALL, intentFilter, null, mLocationHandler);
+ }, UserHandle.ALL, intentFilter, null, mHandler);
+ }
+
+ // 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));
+ }
+
+ // 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);
+ }
+ }
+
+ // perform removal outside of mReceivers loop
+ if (deadReceivers != null) {
+ for (Receiver receiver : deadReceivers) {
+ removeUpdates(receiver);
+ }
+ }
}
private void onUidImportanceChanged(int uid, int importance) {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
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);
-
- if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
- affectedProviders.add(provider);
- }
- }
- }
- }
- for (String provider : affectedProviders) {
- applyRequirementsLocked(provider);
- }
-
- for (Entry<IBinder, Identity> entry : mGnssMeasurementsListeners.entrySet()) {
- Identity callerIdentity = entry.getValue();
- if (callerIdentity.mUid == uid) {
+ 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)"));
- }
- if (foreground || isThrottlingExemptLocked(entry.getValue())) {
- mGnssMeasurementsProvider.addListener(
- IGnssMeasurementsListener.Stub.asInterface(entry.getKey()),
- callerIdentity.mUid, callerIdentity.mPackageName);
- } else {
- mGnssMeasurementsProvider.removeListener(
- IGnssMeasurementsListener.Stub.asInterface(entry.getKey()));
- }
- }
- }
-
- 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 "
+ Log.d(TAG, "request 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()));
+ record.updateForeground(foreground);
+
+ if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
+ affectedProviders.add(provider);
}
}
}
+ }
+ for (String provider : affectedProviders) {
+ applyRequirements(provider);
+ }
- // TODO(b/120449926): The GNSS status listeners should be handled similar to the above.
+ 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()));
+ }
+ }
+ }
+
+ 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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 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 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 @@
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 @@
@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 @@
// 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 @@
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 @@
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 @@
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 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 @@
}
@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 @@
}
}
-
- /**
- * Returns the model name of the GNSS hardware.
- */
@Override
@Nullable
public String getGnssHardwareModelName() {
@@ -1306,10 +1345,6 @@
}
}
- /**
- * 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 @@
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 @@
}
}
- /**
- * 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 @@
}
}
- /**
- * Removes callback for GNSS batching
- */
@Override
public void removeGnssBatchingCallback() {
try {
@@ -1408,10 +1433,6 @@
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 @@
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 @@
}
}
- /**
- * Stops GNSS batching
- */
@Override
public boolean stopGnssBatch() {
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1475,7 +1490,7 @@
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 @@
}
}
- 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 @@
}
}
-
- /**
- * 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 @@
}
}
- /**
- * 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 @@
}
}
- /**
- * 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 @@
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 @@
}
}
- /**
- * 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 @@
*/
@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 @@
* 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 @@
* 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;
- }
- providers = getProviders(null, 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);
}
- if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + null);
+ if (!providers.isEmpty()) {
+ 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);
+ }
+ }
+
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 @@
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 @@
}
@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 @@
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 @@
// and also remove the Receiver if it has no more update records
if (receiverRecords.size() == 0) {
- removeUpdatesLocked(mReceiver);
+ removeUpdates(mReceiver);
}
}
@@ -2119,8 +2043,9 @@
}
}
- 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 @@
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 @@
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 @@
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 @@
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 @@
}
if (provider.isEnabled()) {
- applyRequirementsLocked(name);
+ applyRequirements(name);
} else {
// Notify the listener that updates are currently disabled
receiver.callProviderEnabledLocked(name, false);
@@ -2305,27 +2221,31 @@
@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);
-
- // providers may use public location API's, need to clear identity
- long identity = Binder.clearCallingIdentity();
- try {
- removeUpdatesLocked(receiver);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ 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, 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 @@
// 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 @@
}
return null;
}
-
- 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;
-
- if (!isAllowedByUserSettingsLocked(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;
- }
-
- // 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;
- }
-
- 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);
}
+
+ 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 (!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;
+ }
+
+ // 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);
+ }
+ return null;
+ });
}
- /**
- * 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 @@
}
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, "Location disabled in Settings for current user:" + mCurrentUserId);
+ 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);
}
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 @@
return false;
}
}
- }
- return true;
+ return true;
+ });
}
@Override
@@ -2504,7 +2412,9 @@
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 @@
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 @@
@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 @@
@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 @@
}
return true;
- }
+ });
}
@Override
@@ -2619,28 +2534,30 @@
}
@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 @@
}
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 @@
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 @@
}
}
- /**
- * @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 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);
- }
-
- if (p == null) {
- return null;
- }
- if (p.mProvider instanceof LocationProviderProxy) {
- return ((LocationProviderProxy) p.mProvider).getConnectedPackageName();
- }
- return null;
+ return runFromBinderBlocking(() -> {
+ LocationProvider p = mProvidersByName.get(provider);
+ if (p == null) return null;
+ return p.getProperties();
+ });
}
@Override
- public void setLocationControllerExtraPackage(String packageName) {
- mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
- Manifest.permission.LOCATION_HARDWARE + " permission required");
- synchronized (mLock) {
- mLocationControllerExtraPackage = packageName;
- }
- }
+ public String getNetworkProviderPackage() throws RemoteException {
+ return runFromBinderBlocking(() -> {
+ LocationProvider p = mProvidersByName.get(LocationManager.NETWORK_PROVIDER);
- @Override
- public String getLocationControllerExtraPackage() {
- synchronized (mLock) {
- return mLocationControllerExtraPackage;
- }
- }
-
- @Override
- public void setLocationControllerExtraPackageEnabled(boolean enabled) {
- mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
- Manifest.permission.LOCATION_HARDWARE + " permission required");
- synchronized (mLock) {
- mLocationControllerExtraPackageEnabled = enabled;
- }
- }
-
- @Override
- public boolean isLocationControllerExtraPackageEnabled() {
- synchronized (mLock) {
- return 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) {
- // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- checkInteractAcrossUsersPermission(userId);
-
- 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;
+ if (p == null) {
+ return null;
}
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ if (p.mProvider instanceof LocationProviderProxy) {
+ return ((LocationProviderProxy) p.mProvider).getConnectedPackageName();
+ }
+ return null;
+ });
}
- /**
- * 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 setLocationControllerExtraPackage(String packageName) throws RemoteException {
+ mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+ Manifest.permission.LOCATION_HARDWARE + " permission required");
+
+ runFromBinderBlocking(() -> mLocationControllerExtraPackage = packageName);
+ }
+
+ @Override
+ public String getLocationControllerExtraPackage() throws RemoteException {
+ return runFromBinderBlocking(() -> mLocationControllerExtraPackage);
+ }
+
+ @Override
+ public void setLocationControllerExtraPackageEnabled(boolean enabled) throws RemoteException {
+ mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+ Manifest.permission.LOCATION_HARDWARE + " permission required");
+ runFromBinderBlocking(() -> mLocationControllerExtraPackageEnabled = enabled);
+ }
+
+ @Override
+ public boolean isLocationControllerExtraPackageEnabled() throws RemoteException {
+ return runFromBinderBlocking(() -> mLocationControllerExtraPackageEnabled
+ && (mLocationControllerExtraPackage != null));
+ }
+
+ @Override
+ public boolean isLocationEnabledForUser(int userId) throws RemoteException {
+ // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
+ if (UserHandle.getCallingUserId() != userId) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS,
+ "Requires INTERACT_ACROSS_USERS permission");
+ }
+
+ 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;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ 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(",");
- }
- // Remove the trailing comma
- locationProvidersAllowed.setLength(locationProvidersAllowed.length() - 1);
- Settings.Secure.putStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- locationProvidersAllowed.toString(),
- userId);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
+ 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;
+ }
+ locationProvidersAllowed.append(prefix);
+ locationProvidersAllowed.append(provider);
+ locationProvidersAllowed.append(",");
+ }
+ // 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 @@
// 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 @@
"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 @@
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 @@
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 @@
// 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 @@
// 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 @@
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);
- }
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ 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);
- }
- }
-
- 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 @@
}
@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 @@
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 @@
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 @@
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/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 9690ba8..2ac04d1 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.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 @@
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 @@
// 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 @@
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 @@
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 3420b26..e3a4084 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 @@
}
// 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/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 982daa5..3cb7714 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 @@
"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 fe89be6..522ab0b 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 @@
* 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 @@
*/
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 25169a2..6134d30 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 @@
/** 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/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
index f262f6d..a60f16d 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.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 @@
*/
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 @@
}
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 @@
}
}
+ 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 f008770..01bff07 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 @@
}
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 @@
}
if (DEBUG) {
- Slog.v(TAG, "notifyWallpaperColorsChanged " + which);
+ Slog.v(TAG, "notifyWallpaperColorsChangedOnDisplay " + which);
}
needsExtraction = wallpaper.primaryColors == null;
@@ -371,7 +393,7 @@
// 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 @@
return;
}
}
- notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId);
+ notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId, displayId);
}
}
@@ -390,14 +412,14 @@
}
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 @@
}
}
- 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 @@
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 @@
}
}
+ 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 @@
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 @@
/**
* 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 @@
/**
* 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 @@
// 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 @@
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 @@
FgThread.getHandler().post(() -> {
notifyWallpaperColorsChanged(systemWallpaper, FLAG_SYSTEM);
notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK);
+ notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
});
}
@@ -1743,6 +1811,7 @@
// When clearing a wallpaper, broadcast new valid colors
if (data != null) {
notifyWallpaperColorsChanged(data, which);
+ notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
}
}
@@ -2084,35 +2153,47 @@
}
@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);
}
- userColorsChangedListeners.register(cb);
+ RemoteCallbackList<IWallpaperManagerCallback> displayChangedListeners =
+ userDisplayColorsChangedListeners.get(displayId);
+ if (displayChangedListeners == null) {
+ displayChangedListeners = new RemoteCallbackList<>();
+ userDisplayColorsChangedListeners.put(displayId, displayChangedListeners);
+ }
+ 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 @@
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 @@
}
@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 @@
// 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 @@
}
}
+ 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 @@
if (shouldNotifyColors) {
notifyWallpaperColorsChanged(wallpaper, which);
+ notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 8884615..6f8f85f 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 @@
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 @@
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 @@
// 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 @@
// 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/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index fecc8da..740d472 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 @@
}
}
+ /** @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/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index e343322..1d56f04 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_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 @@
setFrontOfTask();
}
- void addActivityAtBottom(ActivityRecord r) {
- addActivityAtIndex(0, r);
- }
-
void addActivityToTop(ActivityRecord r) {
addActivityAtIndex(mActivities.size(), r);
}
@@ -1278,6 +1277,34 @@
}
/**
+ * 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 @@
updateTaskDescription();
}
- private void adjustForMinimalTaskDimensions(Rect bounds, Rect previousBounds) {
+ void adjustForMinimalTaskDimensions(Rect bounds, Rect previousBounds) {
if (bounds == null) {
return;
}
@@ -1853,11 +1880,27 @@
@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 @@
* 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 @@
}
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 @@
// 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 @@
}
}
- // 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());
- }
-
- computeConfigResourceOverrides(inOutConfig, outOverrideBounds, parentConfig);
- }
- }
-
@Override
void resolveOverrideConfiguration(Configuration newParentConfig) {
- computeResolvedOverrideConfiguration(getResolvedOverrideConfiguration(), newParentConfig,
- getRequestedOverrideConfiguration());
+ 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);
+ }
+ }
+ }
+
+ if (outOverrideBounds.isEmpty()) {
+ // If the task fills the parent, just inherit all the other configs from parent.
+ return;
+ }
+
+ 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/net/java/android/net/dhcp/DhcpServingParams.java b/services/net/java/android/net/dhcp/DhcpServingParams.java
index df15ba1..2780814a 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 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 @@
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 @@
* 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 @@
* <p>This parameter is required.
*/
public Builder setServerAddr(@NonNull LinkAddress serverAddr) {
- this.serverAddr = serverAddr;
+ this.mServerAddr = serverAddr;
return this;
}
@@ -159,7 +192,7 @@
* 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 @@
* {@link DhcpServingParams}.
*/
public Builder setDnsServers(@NonNull Set<Inet4Address> dnsServers) {
- this.dnsServers = dnsServers;
+ this.mDnsServers = dnsServers;
return this;
}
@@ -219,7 +252,7 @@
* 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 @@
* <p>This parameter is required.
*/
public Builder setDhcpLeaseTimeSecs(long dhcpLeaseTimeSecs) {
- this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs;
+ this.mDhcpLeaseTimeSecs = dhcpLeaseTimeSecs;
return this;
}
@@ -250,7 +283,7 @@
* 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 @@
* <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 @@
*/
@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 0000000..f068c3a
--- /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/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 9e3a0ea..147a691 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_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;
@@ -123,6 +122,11 @@
@Override
void wakeUp() {}
+
+ @Override
+ boolean isControlEnabled() {
+ return true;
+ }
};
mMyLooper = mTestLooper.getLooper();
@@ -144,7 +148,7 @@
}
@Test
- public void handleGiveAudioStatus_volume_10_mute_true() {
+ public void handleGiveAudioStatus_volume_10_mute_true() throws Exception {
mMusicVolume = 10;
mMusicMute = true;
mMusicMaxVolume = 20;
@@ -161,7 +165,7 @@
}
@Test
- public void handleGiveSystemAudioModeStatus_originalOff() {
+ public void handleGiveSystemAudioModeStatus_originalOff() throws Exception {
HdmiCecMessage expectedMessage =
HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
HdmiCecMessage messageGive =
@@ -228,7 +232,7 @@
}
@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);
@@ -256,7 +260,7 @@
@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 =
@@ -282,7 +286,7 @@
@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
@@ -296,7 +300,7 @@
}
@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 +311,7 @@
}
@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 +322,7 @@
}
@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 +333,7 @@
}
@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,7 +344,7 @@
}
@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))
@@ -351,7 +355,7 @@
}
@Test
- public void terminateSystemAudioMode_systemAudioModeOff() {
+ public void terminateSystemAudioMode_systemAudioModeOff() throws Exception {
mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(false);
assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isFalse();
mMusicMute = false;
@@ -366,7 +370,7 @@
@Ignore("b/80297700")
@Test
- public void terminateSystemAudioMode_systemAudioModeOn() {
+ public void terminateSystemAudioMode_systemAudioModeOn() throws Exception {
mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isTrue();
mMusicMute = false;
@@ -381,7 +385,7 @@
}
@Test
- public void isPhysicalAddressMeOrBelow_isMe() {
+ public void isPhysicalAddressMeOrBelow_isMe() throws Exception {
int targetPhysicalAddress = 0x1000;
mNativeWrapper.setPhysicalAddress(0x1000);
assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
@@ -389,7 +393,7 @@
}
@Test
- public void isPhysicalAddressMeOrBelow_isBelow() {
+ public void isPhysicalAddressMeOrBelow_isBelow() throws Exception {
int targetPhysicalAddress = 0x1100;
mNativeWrapper.setPhysicalAddress(0x1000);
assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
@@ -397,7 +401,7 @@
}
@Test
- public void isPhysicalAddressMeOrBelow_neitherMeNorBelow() {
+ public void isPhysicalAddressMeOrBelow_neitherMeNorBelow() throws Exception {
int targetPhysicalAddress = 0x3000;
mNativeWrapper.setPhysicalAddress(0x2000);
assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
@@ -420,7 +424,7 @@
}
@Test
- public void handleRequestArcInitiate_isNotDirectConnectedToTv() {
+ public void handleRequestArcInitiate_isNotDirectConnectedToTv() throws Exception {
HdmiCecMessage message = HdmiCecMessageBuilder
.buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
@@ -437,7 +441,7 @@
}
@Test
- public void handleRequestArcInitiate_startArcInitiationActionFromAvr() {
+ public void handleRequestArcInitiate_startArcInitiationActionFromAvr() throws Exception {
HdmiCecMessage message = HdmiCecMessageBuilder
.buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
mNativeWrapper.setPhysicalAddress(0x1000);
@@ -452,7 +456,7 @@
}
@Test
- public void handleRequestArcTerminate_arcIsOn_startTerminationActionFromAvr() {
+ public void handleRequestArcTerminate_arcIsOn_startTerminationActionFromAvr() throws Exception {
mHdmiCecLocalDeviceAudioSystem.setArcStatus(true);
assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isTrue();
@@ -469,7 +473,7 @@
}
@Test
- public void handleRequestArcTerminate_arcIsNotOn() {
+ public void handleRequestArcTerminate_arcIsNotOn() throws Exception {
assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
HdmiCecMessage message = HdmiCecMessageBuilder
.buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
@@ -486,7 +490,7 @@
}
@Test
- public void handleRequestArcInit_arcIsNotSupported() {
+ public void handleRequestArcInit_arcIsNotSupported() throws Exception {
HdmiCecMessage message = HdmiCecMessageBuilder
.buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
@@ -501,4 +505,15 @@
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(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/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 569c6d4..fe5840f 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.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.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;
@@ -144,6 +148,13 @@
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 +245,10 @@
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 +361,7 @@
mStack.addTask(task, true, "creating test task");
task.setStack(mStack);
task.setTask();
+ mStack.getWindowContainerController().mContainer.addChild(task.mTask, 0);
}
task.touchActiveTime();
@@ -365,7 +381,10 @@
setTask();
}
- private void setTask() {
+ void setTask() {
+ Task mockTask = mock(Task.class);
+ mockTask.mTaskRecord = this;
+ doCallRealMethod().when(mockTask).onDescendantOrientationChanged(any(), any());
setTask(mock(Task.class));
}
}
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 7da85af..00bec3f 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_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 @@
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 @@
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/tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java b/tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java
new file mode 100644
index 0000000..4a6f20a
--- /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 b6a4073..2ab2246 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 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.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 @@
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 @@
.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 @@
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 @@
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/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 7b2f07d..fef0ed7 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -64,7 +64,7 @@
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/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index f2a3b42..ad2ed81 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1234,12 +1234,13 @@
* 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 2396108..c505a58 100644
--- a/wifi/java/com/android/server/wifi/AbstractWifiService.java
+++ b/wifi/java/com/android/server/wifi/AbstractWifiService.java
@@ -21,6 +21,7 @@
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.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 @@
}
@Override
- public List<OsuProvider> getMatchingOsuProviders(List<ScanResult> scanResults) {
+ public Map<OsuProvider, List<ScanResult>> getMatchingOsuProviders(
+ List<ScanResult> scanResults) {
throw new UnsupportedOperationException();
}
@@ -470,4 +473,21 @@
public String[] getFactoryMacAddresses() {
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();
+ }
}