summaryrefslogtreecommitdiffstats
path: root/services/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'services/core/java')
-rw-r--r--services/core/java/com/android/server/AssetAtlasService.java118
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java283
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java258
-rw-r--r--services/core/java/com/android/server/MountService.java227
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java66
-rw-r--r--services/core/java/com/android/server/Watchdog.java17
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java145
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java34
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java191
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java77
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java31
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkMonitor.java2
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java29
-rw-r--r--services/core/java/com/android/server/display/DisplayDevice.java4
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceInfo.java63
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java27
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java5
-rw-r--r--services/core/java/com/android/server/fingerprint/FingerprintService.java379
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java6
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java6
-rw-r--r--services/core/java/com/android/server/hdmi/HotplugDetectionAction.java11
-rw-r--r--services/core/java/com/android/server/job/controllers/AppIdleController.java11
-rw-r--r--services/core/java/com/android/server/location/GpsLocationProvider.java160
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java110
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java29
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java3
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java3
-rw-r--r--services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java3
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java7
-rw-r--r--services/core/java/com/android/server/webkit/WebViewUpdateService.java21
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java1
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java2
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java4
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java2
-rw-r--r--services/core/java/com/android/server/wm/Task.java4
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java13
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java69
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java9
38 files changed, 1821 insertions, 609 deletions
diff --git a/services/core/java/com/android/server/AssetAtlasService.java b/services/core/java/com/android/server/AssetAtlasService.java
index 66cc29a..26f4232 100644
--- a/services/core/java/com/android/server/AssetAtlasService.java
+++ b/services/core/java/com/android/server/AssetAtlasService.java
@@ -199,11 +199,6 @@ public class AssetAtlasService extends IAssetAtlas.Stub {
private final ArrayList<Bitmap> mBitmaps;
private final int mPixelCount;
- private long mNativeBitmap;
-
- // Used for debugging only
- private Bitmap mAtlasBitmap;
-
Renderer(ArrayList<Bitmap> bitmaps, int pixelCount) {
mBitmaps = bitmaps;
mPixelCount = pixelCount;
@@ -258,8 +253,9 @@ public class AssetAtlasService extends IAssetAtlas.Stub {
// We always render the atlas into a bitmap. This bitmap is then
// uploaded into the GraphicBuffer using OpenGL to swizzle the content
- final Canvas canvas = acquireCanvas(buffer.getWidth(), buffer.getHeight());
- if (canvas == null) return false;
+ final Bitmap atlasBitmap = Bitmap.createBitmap(
+ buffer.getWidth(), buffer.getHeight(), Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(atlasBitmap);
final Atlas.Entry entry = new Atlas.Entry();
@@ -268,108 +264,78 @@ public class AssetAtlasService extends IAssetAtlas.Stub {
int mapIndex = 0;
boolean result = false;
- try {
- final long startRender = System.nanoTime();
- final int count = mBitmaps.size();
-
- for (int i = 0; i < count; i++) {
- final Bitmap bitmap = mBitmaps.get(i);
- if (atlas.pack(bitmap.getWidth(), bitmap.getHeight(), entry) != null) {
- // We have more bitmaps to pack than the current configuration
- // says, we were most likely not able to detect a change in the
- // list of preloaded drawables, abort and delete the configuration
- if (mapIndex >= mAtlasMap.length) {
- deleteDataFile();
- break;
- }
+ final long startRender = System.nanoTime();
+ final int count = mBitmaps.size();
- canvas.save();
- canvas.translate(entry.x, entry.y);
- if (entry.rotated) {
- canvas.translate(bitmap.getHeight(), 0.0f);
- canvas.rotate(90.0f);
- }
- canvas.drawBitmap(bitmap, 0.0f, 0.0f, null);
- canvas.restore();
- atlasMap[mapIndex++] = bitmap.getSkBitmap();
- atlasMap[mapIndex++] = entry.x;
- atlasMap[mapIndex++] = entry.y;
- atlasMap[mapIndex++] = entry.rotated ? 1 : 0;
+ for (int i = 0; i < count; i++) {
+ final Bitmap bitmap = mBitmaps.get(i);
+ if (atlas.pack(bitmap.getWidth(), bitmap.getHeight(), entry) != null) {
+ // We have more bitmaps to pack than the current configuration
+ // says, we were most likely not able to detect a change in the
+ // list of preloaded drawables, abort and delete the configuration
+ if (mapIndex >= mAtlasMap.length) {
+ deleteDataFile();
+ break;
}
- }
- final long endRender = System.nanoTime();
- if (mNativeBitmap != 0) {
- result = nUploadAtlas(buffer, mNativeBitmap);
- }
-
- final long endUpload = System.nanoTime();
- if (DEBUG_ATLAS) {
- float renderDuration = (endRender - startRender) / 1000.0f / 1000.0f;
- float uploadDuration = (endUpload - endRender) / 1000.0f / 1000.0f;
- Log.d(LOG_TAG, String.format("Rendered atlas in %.2fms (%.2f+%.2fms)",
- renderDuration + uploadDuration, renderDuration, uploadDuration));
+ canvas.save();
+ canvas.translate(entry.x, entry.y);
+ if (entry.rotated) {
+ canvas.translate(bitmap.getHeight(), 0.0f);
+ canvas.rotate(90.0f);
+ }
+ canvas.drawBitmap(bitmap, 0.0f, 0.0f, null);
+ canvas.restore();
+ atlasMap[mapIndex++] = bitmap.refSkPixelRef();
+ atlasMap[mapIndex++] = entry.x;
+ atlasMap[mapIndex++] = entry.y;
+ atlasMap[mapIndex++] = entry.rotated ? 1 : 0;
}
+ }
- } finally {
- releaseCanvas(canvas);
+ final long endRender = System.nanoTime();
+ releaseCanvas(canvas, atlasBitmap);
+ result = nUploadAtlas(buffer, atlasBitmap);
+ atlasBitmap.recycle();
+ final long endUpload = System.nanoTime();
+
+ if (DEBUG_ATLAS) {
+ float renderDuration = (endRender - startRender) / 1000.0f / 1000.0f;
+ float uploadDuration = (endUpload - endRender) / 1000.0f / 1000.0f;
+ Log.d(LOG_TAG, String.format("Rendered atlas in %.2fms (%.2f+%.2fms)",
+ renderDuration + uploadDuration, renderDuration, uploadDuration));
}
return result;
}
/**
- * Returns a Canvas for the specified buffer. If {@link #DEBUG_ATLAS_TEXTURE}
- * is turned on, the returned Canvas will render into a local bitmap that
- * will then be saved out to disk for debugging purposes.
- * @param width
- * @param height
- */
- private Canvas acquireCanvas(int width, int height) {
- if (DEBUG_ATLAS_TEXTURE) {
- mAtlasBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- return new Canvas(mAtlasBitmap);
- } else {
- Canvas canvas = new Canvas();
- mNativeBitmap = nAcquireAtlasCanvas(canvas, width, height);
- return canvas;
- }
- }
-
- /**
* Releases the canvas used to render into the buffer. Calling this method
* will release any resource previously acquired. If {@link #DEBUG_ATLAS_TEXTURE}
* is turend on, calling this method will write the content of the atlas
* to disk in /data/system/atlas.png for debugging.
*/
- private void releaseCanvas(Canvas canvas) {
+ private void releaseCanvas(Canvas canvas, Bitmap atlasBitmap) {
+ canvas.setBitmap(null);
if (DEBUG_ATLAS_TEXTURE) {
- canvas.setBitmap(null);
File systemDirectory = new File(Environment.getDataDirectory(), "system");
File dataFile = new File(systemDirectory, "atlas.png");
try {
FileOutputStream out = new FileOutputStream(dataFile);
- mAtlasBitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
+ atlasBitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
out.close();
} catch (FileNotFoundException e) {
// Ignore
} catch (IOException e) {
// Ignore
}
-
- mAtlasBitmap.recycle();
- mAtlasBitmap = null;
- } else {
- nReleaseAtlasCanvas(canvas, mNativeBitmap);
}
}
}
- private static native long nAcquireAtlasCanvas(Canvas canvas, int width, int height);
- private static native void nReleaseAtlasCanvas(Canvas canvas, long bitmap);
- private static native boolean nUploadAtlas(GraphicBuffer buffer, long bitmap);
+ private static native boolean nUploadAtlas(GraphicBuffer buffer, Bitmap bitmap);
@Override
public boolean isCompatible(int ppid) {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index ef51ad6..3e5eee8 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -112,6 +112,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
private static final int SERVICE_IBLUETOOTHGATT = 2;
private final Context mContext;
+ private static int mBleAppCount = 0;
// Locks are not provided for mName and mAddress.
// They are accessed in handler or broadcast receiver, same thread context.
@@ -184,11 +185,40 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
}
}
+
+ int st = BluetoothAdapter.STATE_OFF;
+ if (mBluetooth != null) {
+ try {
+ st = mBluetooth.getState();
+ } catch (RemoteException e) {
+ Log.e(TAG,"Unable to call getState", e);
+ }
+ }
+ Log.d(TAG, "state" + st);
+
if (isAirplaneModeOn()) {
- // disable without persisting the setting
- sendDisableMsg();
+ // Clear registered LE apps to force shut-off
+ synchronized (this) {
+ mBleAppCount = 0;
+ }
+ if (st == BluetoothAdapter.STATE_BLE_ON) {
+ //if state is BLE_ON make sure you trigger disableBLE part
+ try {
+ if (mBluetooth != null) {
+ mBluetooth.onBrEdrDown();
+ mEnableExternal = false;
+ }
+ } catch(RemoteException e) {
+ Log.e(TAG,"Unable to call onBrEdrDown", e);
+ }
+ } else if (st == BluetoothAdapter.STATE_ON){
+ // disable without persisting the setting
+ Log.d(TAG, "Calling disable");
+ sendDisableMsg();
+ }
} else if (mEnableExternal) {
// enable without persisting the setting
+ Log.d(TAG, "Calling enable");
sendEnableMsg(mQuietEnableExternal);
}
}
@@ -203,12 +233,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
sendEnableMsg(mQuietEnableExternal);
}
}
-
- if (!isNameAndAddressSet()) {
- //Sync the Bluetooth name and address from the Bluetooth Adapter
- if (DBG) Log.d(TAG,"Retrieving Bluetooth Adapter name and address...");
- getNameAndAddress();
- }
}
}
};
@@ -218,6 +242,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
mContext = context;
mBluetooth = null;
+ mBluetoothGatt = null;
mBinding = false;
mUnbinding = false;
mEnable = false;
@@ -396,6 +421,133 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
return false;
}
+ class ClientDeathRecipient implements IBinder.DeathRecipient {
+ public void binderDied() {
+ if (DBG) Log.d(TAG, "Binder is dead - unregister Ble App");
+ if (mBleAppCount > 0) --mBleAppCount;
+
+ if (mBleAppCount == 0) {
+ if (DBG) Log.d(TAG, "Disabling LE only mode after application crash");
+ try {
+ if (mBluetooth != null) {
+ mBluetooth.onBrEdrDown();
+ }
+ } catch(RemoteException e) {
+ Log.e(TAG,"Unable to call onBrEdrDown", e);
+ }
+ }
+ }
+ }
+
+ /** Internal death rec list */
+ Map<IBinder, ClientDeathRecipient> mBleApps = new HashMap<IBinder, ClientDeathRecipient>();
+
+ public int updateBleAppCount(IBinder token, boolean enable) {
+ if (enable) {
+ ClientDeathRecipient r = mBleApps.get(token);
+ if (r == null) {
+ ClientDeathRecipient deathRec = new ClientDeathRecipient();
+ try {
+ token.linkToDeath(deathRec, 0);
+ } catch (RemoteException ex) {
+ throw new IllegalArgumentException("Wake lock is already dead.");
+ }
+ mBleApps.put(token, deathRec);
+ synchronized (this) {
+ ++mBleAppCount;
+ }
+ if (DBG) Log.d(TAG, "Registered for death Notification");
+ }
+
+ } else {
+ ClientDeathRecipient r = mBleApps.get(token);
+ if (r != null) {
+ try {
+ token.linkToDeath(r, 0);
+ } catch (RemoteException ex) {
+ throw new IllegalArgumentException("Wake lock is already dead.");
+ }
+ mBleApps.remove(token);
+ synchronized (this) {
+ if (mBleAppCount > 0) --mBleAppCount;
+ }
+ if (DBG) Log.d(TAG, "Unregistered for death Notification");
+ }
+ }
+ if (DBG) Log.d(TAG, "Updated BleAppCount" + mBleAppCount);
+ if (mBleAppCount == 0 && mEnable) {
+ try {
+ if (mBluetooth != null && (mBluetooth.getState() != BluetoothAdapter.STATE_ON)) {
+ if (DBG) Log.d(TAG, "Reseting the mEnable flag for clean disable");
+ mEnable = false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "getState()", e);
+ }
+ }
+ return mBleAppCount;
+ }
+
+ /** @hide*/
+ public boolean isBleAppPresent() {
+ if (DBG) Log.d(TAG, "isBleAppPresent() count: " + mBleAppCount);
+ return (mBleAppCount > 0);
+ }
+
+ /**
+ * Action taken when GattService is turned off
+ */
+ private void onBluetoothGattServiceUp() {
+ if (DBG) Log.d(TAG,"BluetoothGatt Service is Up");
+ try{
+ if (isBleAppPresent() == false && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
+ mBluetooth.onLeServiceUp();
+
+ // waive WRITE_SECURE_SETTINGS permission check
+ long callingIdentity = Binder.clearCallingIdentity();
+ persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ } catch(RemoteException e) {
+ Log.e(TAG,"Unable to call onServiceUp", e);
+ }
+ }
+
+ /**
+ * Inform BluetoothAdapter instances that BREDR part is down
+ * and turn off all service and stack if no LE app needs it
+ */
+ private void sendBrEdrDownCallback() {
+ if (DBG) Log.d(TAG,"Calling sendBrEdrDownCallback callbacks");
+ int n = mCallbacks.beginBroadcast();
+
+ if (isBleAppPresent() == false) {
+ try {
+ mBluetooth.onBrEdrDown();
+ } catch(RemoteException e) {
+ Log.e(TAG,"Unable to call onBrEdrDown", e);
+ }
+ }
+ else{//need to stay at BLE ON. disconnect all Gatt connections
+ try{
+ mBluetoothGatt.unregAll();//disconnectAll();
+ } catch(RemoteException e) {
+ Log.e(TAG,"Unable to disconn all", e);
+ }
+ }
+
+ Log.d(TAG,"Broadcasting onBrEdrDown() to " + n + " receivers.");
+ for (int i=0; i <n; i++) {
+ try {
+ mCallbacks.getBroadcastItem(i).onBrEdrDown();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to call sendBrEdrDownCallback() on callback #" + i, e);
+ }
+ }
+ mCallbacks.finishBroadcast();
+ }
+
+ /** @hide*/
public void getNameAndAddress() {
if (DBG) {
Log.d(TAG,"getNameAndAddress(): mBluetooth = " + mBluetooth +
@@ -445,11 +597,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
mQuietEnableExternal = false;
mEnableExternal = true;
// waive WRITE_SECURE_SETTINGS permission check
- long callingIdentity = Binder.clearCallingIdentity();
- persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
- Binder.restoreCallingIdentity(callingIdentity);
sendEnableMsg(false);
}
+ if (DBG) Log.d(TAG, "enable returning");
return true;
}
@@ -508,6 +658,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
} else {
mUnbinding=false;
}
+ mBluetoothGatt = null;
}
}
@@ -1034,6 +1185,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
synchronized(mConnection) {
if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
mBluetoothGatt = IBluetoothGatt.Stub.asInterface(service);
+ onBluetoothGattServiceUp();
break;
} // else must be SERVICE_IBLUETOOTH
@@ -1111,12 +1263,18 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
bluetoothStateChangeHandler(prevState, newState);
// handle error state transition case from TURNING_ON to OFF
// unbind and rebind bluetooth service and enable bluetooth
- if ((prevState == BluetoothAdapter.STATE_TURNING_ON) &&
+ if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_ON) &&
(newState == BluetoothAdapter.STATE_OFF) &&
(mBluetooth != null) && mEnable) {
recoverBluetoothServiceFromError();
}
- if (newState == BluetoothAdapter.STATE_ON) {
+ if ((prevState == BluetoothAdapter.STATE_TURNING_ON) &&
+ (newState == BluetoothAdapter.STATE_BLE_ON) &&
+ (mBluetooth != null) && mEnable) {
+ recoverBluetoothServiceFromError();
+ }
+ if (newState == BluetoothAdapter.STATE_ON ||
+ newState == BluetoothAdapter.STATE_BLE_ON) {
// bluetooth is working, reset the counter
if (mErrorRecoveryRetryCounter != 0) {
Log.w(TAG, "bluetooth is recovered from error");
@@ -1376,39 +1534,90 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
return valid;
}
+ private void sendBleStateChanged(int prevState, int newState) {
+ if (DBG) Log.d(TAG,"BLE State Change Intent: " + prevState + " -> " + newState);
+ // Send broadcast message to everyone else
+ Intent intent = new Intent(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
+ intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
+ intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM);
+ }
+
private void bluetoothStateChangeHandler(int prevState, int newState) {
+ boolean isStandardBroadcast = true;
if (prevState != newState) {
//Notify all proxy objects first of adapter state change
- if (newState == BluetoothAdapter.STATE_ON || newState == BluetoothAdapter.STATE_OFF) {
- boolean isUp = (newState==BluetoothAdapter.STATE_ON);
- sendBluetoothStateCallback(isUp);
-
- if (isUp) {
- // connect to GattService
- if (mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_BLUETOOTH_LE)) {
- Intent i = new Intent(IBluetoothGatt.class.getName());
- doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
- UserHandle.CURRENT);
- }
- } else {
- //If Bluetooth is off, send service down event to proxy objects, and unbind
- if (!isUp && canUnbindBluetoothService()) {
- unbindAllBluetoothProfileServices();
+ if (newState == BluetoothAdapter.STATE_BLE_ON
+ || newState == BluetoothAdapter.STATE_OFF) {
+ boolean intermediate_off = (prevState == BluetoothAdapter.STATE_TURNING_OFF
+ && newState == BluetoothAdapter.STATE_BLE_ON);
+
+ if (newState == BluetoothAdapter.STATE_OFF) {
+ // If Bluetooth is off, send service down event to proxy objects, and unbind
+ if (DBG) Log.d(TAG, "Bluetooth is complete turn off");
+ if (canUnbindBluetoothService()) {
+ if (DBG) Log.d(TAG, "Good to unbind!");
sendBluetoothServiceDownCallback();
unbindAndFinish();
+ sendBleStateChanged(prevState, newState);
+ // Don't broadcast as it has already been broadcast before
+ isStandardBroadcast = false;
}
+
+ } else if (!intermediate_off) {
+ // connect to GattService
+ if (DBG) Log.d(TAG, "Bluetooth is in LE only mode");
+ if (mBluetoothGatt != null) {
+ if (DBG) Log.d(TAG, "Calling BluetoothGattServiceUp");
+ onBluetoothGattServiceUp();
+ } else {
+ if (DBG) Log.d(TAG, "Binding Bluetooth GATT service");
+ if (mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_BLUETOOTH_LE)) {
+ Intent i = new Intent(IBluetoothGatt.class.getName());
+ doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT);
+ }
+ }
+ sendBleStateChanged(prevState, newState);
+ //Don't broadcase this as std intent
+ isStandardBroadcast = false;
+
+ } else if (intermediate_off){
+ if (DBG) Log.d(TAG, "Intermediate off, back to LE only mode");
+ // For LE only mode, broadcast as is
+ sendBleStateChanged(prevState, newState);
+ sendBluetoothStateCallback(false); // BT is OFF for general users
+ // Broadcast as STATE_OFF
+ newState = BluetoothAdapter.STATE_OFF;
+ sendBrEdrDownCallback();
}
+ } else if (newState == BluetoothAdapter.STATE_ON) {
+ boolean isUp = (newState==BluetoothAdapter.STATE_ON);
+ sendBluetoothStateCallback(isUp);
+ sendBleStateChanged(prevState, newState);
+
+ } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON
+ || newState == BluetoothAdapter.STATE_BLE_TURNING_OFF ) {
+ sendBleStateChanged(prevState, newState);
+ isStandardBroadcast = false;
+
+ } else if (newState == BluetoothAdapter.STATE_TURNING_ON
+ || newState == BluetoothAdapter.STATE_TURNING_OFF) {
+ sendBleStateChanged(prevState, newState);
}
- //Send broadcast message to everyone else
- Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
- intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
- intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- if (DBG) Log.d(TAG,"Bluetooth State Change Intent: " + prevState + " -> " + newState);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
- BLUETOOTH_PERM);
+ if (isStandardBroadcast) {
+ if (prevState == BluetoothAdapter.STATE_BLE_ON) {
+ // Show prevState of BLE_ON as OFF to standard users
+ prevState = BluetoothAdapter.STATE_OFF;
+ }
+ Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
+ intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
+ intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM);
+ }
}
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 1ac1c8a..b785d3d 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -19,6 +19,7 @@ package com.android.server;
import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
+import static android.net.ConnectivityManager.NETID_UNSET;
import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.getNetworkTypeName;
@@ -89,6 +90,7 @@ import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.Xml;
@@ -161,6 +163,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
private static final String NETWORK_RESTORE_DELAY_PROP_NAME =
"android.telephony.apn-restore";
+ // How long to wait before putting up a "This network doesn't have an Internet connection,
+ // connect anyway?" dialog after the user selects a network that doesn't validate.
+ private static final int PROMPT_UNVALIDATED_DELAY_MS = 8 * 1000;
+
// How long to delay to removal of a pending intent based request.
// See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
private final int mReleasePendingIntentDelayMs;
@@ -324,6 +330,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
*/
private static final int EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT = 27;
+ /**
+ * used to specify whether a network should be used even if unvalidated.
+ * arg1 = whether to accept the network if it's unvalidated (1 or 0)
+ * arg2 = whether to remember this choice in the future (1 or 0)
+ * obj = network
+ */
+ private static final int EVENT_SET_ACCEPT_UNVALIDATED = 28;
+
+ /**
+ * used to ask the user to confirm a connection to an unvalidated network.
+ * obj = network
+ */
+ private static final int EVENT_PROMPT_UNVALIDATED = 29;
/** Handler used for internal events. */
final private InternalHandler mHandler;
@@ -692,16 +711,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
return mNextNetworkRequestId++;
}
- private void assignNextNetId(NetworkAgentInfo nai) {
+ private int reserveNetId() {
synchronized (mNetworkForNetId) {
for (int i = MIN_NET_ID; i <= MAX_NET_ID; i++) {
int netId = mNextNetId;
if (++mNextNetId > MAX_NET_ID) mNextNetId = MIN_NET_ID;
// Make sure NetID unused. http://b/16815182
- if (mNetworkForNetId.get(netId) == null) {
- nai.network = new Network(netId);
- mNetworkForNetId.put(netId, nai);
- return;
+ if (!mNetIdInUse.get(netId)) {
+ mNetIdInUse.put(netId, true);
+ return netId;
}
}
}
@@ -722,7 +740,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
info = new NetworkInfo(nai.networkInfo);
lp = new LinkProperties(nai.linkProperties);
nc = new NetworkCapabilities(nai.networkCapabilities);
- network = new Network(nai.network);
+ // Network objects are outwardly immutable so there is no point to duplicating.
+ // Duplicating also precludes sharing socket factories and connection pools.
+ network = nai.network;
subscriberId = (nai.networkMisc != null) ? nai.networkMisc.subscriberId : null;
}
info.setType(networkType);
@@ -790,7 +810,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
info = new NetworkInfo(nai.networkInfo);
lp = new LinkProperties(nai.linkProperties);
nc = new NetworkCapabilities(nai.networkCapabilities);
- network = new Network(nai.network);
+ // Network objects are outwardly immutable so there is no point to duplicating.
+ // Duplicating also precludes sharing socket factories and connection pools.
+ network = nai.network;
subscriberId = (nai.networkMisc != null) ? nai.networkMisc.subscriberId : null;
}
}
@@ -856,6 +878,28 @@ public class ConnectivityService extends IConnectivityManager.Stub
return getFilteredNetworkInfo(state.networkInfo, state.linkProperties, uid);
}
+ @Override
+ public Network getActiveNetwork() {
+ enforceAccessPermission();
+ final int uid = Binder.getCallingUid();
+ final int user = UserHandle.getUserId(uid);
+ int vpnNetId = NETID_UNSET;
+ synchronized (mVpns) {
+ final Vpn vpn = mVpns.get(user);
+ if (vpn != null && vpn.appliesToUid(uid)) vpnNetId = vpn.getNetId();
+ }
+ NetworkAgentInfo nai;
+ if (vpnNetId != NETID_UNSET) {
+ synchronized (mNetworkForNetId) {
+ nai = mNetworkForNetId.get(vpnNetId);
+ }
+ if (nai != null) return nai.network;
+ }
+ nai = getDefaultNetwork();
+ if (nai != null && isNetworkWithLinkPropertiesBlocked(nai.linkProperties, uid)) nai = null;
+ return nai != null ? nai.network : null;
+ }
+
/**
* Find the first Provisioning network.
*
@@ -968,13 +1012,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
@Override
public Network[] getAllNetworks() {
enforceAccessPermission();
- final ArrayList<Network> result = new ArrayList();
synchronized (mNetworkForNetId) {
+ final Network[] result = new Network[mNetworkForNetId.size()];
for (int i = 0; i < mNetworkForNetId.size(); i++) {
- result.add(new Network(mNetworkForNetId.valueAt(i).network));
+ result[i] = mNetworkForNetId.valueAt(i).network;
}
+ return result;
}
- return result.toArray(new Network[result.size()]);
}
private NetworkCapabilities getNetworkCapabilitiesAndValidation(NetworkAgentInfo nai) {
@@ -1843,6 +1887,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
loge("ERROR: created network explicitly selected.");
}
nai.networkMisc.explicitlySelected = true;
+ nai.networkMisc.acceptUnvalidated = (boolean) msg.obj;
break;
}
case NetworkMonitor.EVENT_NETWORK_TESTED: {
@@ -1866,6 +1911,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
android.net.NetworkAgent.CMD_REPORT_NETWORK_STATUS,
(valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
0, null);
+
+ // TODO: trigger a NetworkCapabilities update so that the dialog can know
+ // that the network is now validated and close itself.
}
break;
}
@@ -1939,6 +1987,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (nai != null) {
synchronized (mNetworkForNetId) {
mNetworkForNetId.remove(nai.network.netId);
+ mNetIdInUse.delete(nai.network.netId);
}
// Just in case.
mLegacyTypeTracker.remove(nai);
@@ -1982,6 +2031,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
mLegacyTypeTracker.remove(nai);
synchronized (mNetworkForNetId) {
mNetworkForNetId.remove(nai.network.netId);
+ mNetIdInUse.delete(nai.network.netId);
}
// Since we've lost the network, go through all the requests that
// it was satisfying and see if any other factory can satisfy them.
@@ -2227,6 +2277,91 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
+ public void setAcceptUnvalidated(Network network, boolean accept, boolean always) {
+ enforceConnectivityInternalPermission();
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_ACCEPT_UNVALIDATED,
+ accept ? 1 : 0, always ? 1: 0, network));
+ }
+
+ private void handleSetAcceptUnvalidated(Network network, boolean accept, boolean always) {
+ if (DBG) log("handleSetAcceptUnvalidated network=" + network +
+ " accept=" + accept + " always=" + always);
+
+ NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+ if (nai == null) {
+ // Nothing to do.
+ return;
+ }
+
+ if (nai.everValidated) {
+ // The network validated while the dialog box was up. Don't make any changes. There's a
+ // TODO in the dialog code to make it go away if the network validates; once that's
+ // implemented, taking action here will be confusing.
+ return;
+ }
+
+ if (!nai.networkMisc.explicitlySelected) {
+ Slog.wtf(TAG, "BUG: setAcceptUnvalidated non non-explicitly selected network");
+ }
+
+ if (accept != nai.networkMisc.acceptUnvalidated) {
+ int oldScore = nai.getCurrentScore();
+ nai.networkMisc.acceptUnvalidated = accept;
+ rematchAllNetworksAndRequests(nai, oldScore);
+ sendUpdatedScoreToFactories(nai);
+ }
+
+ if (always) {
+ nai.asyncChannel.sendMessage(
+ NetworkAgent.CMD_SAVE_ACCEPT_UNVALIDATED, accept ? 1 : 0);
+ }
+
+ // TODO: should we also disconnect from the network if accept is false?
+ }
+
+ private void scheduleUnvalidatedPrompt(NetworkAgentInfo nai) {
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(EVENT_PROMPT_UNVALIDATED, nai.network),
+ PROMPT_UNVALIDATED_DELAY_MS);
+ }
+
+ private void handlePromptUnvalidated(Network network) {
+ NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+
+ // Only prompt if the network is unvalidated and was explicitly selected by the user, and if
+ // we haven't already been told to switch to it regardless of whether it validated or not.
+ if (nai == null || nai.everValidated ||
+ !nai.networkMisc.explicitlySelected || nai.networkMisc.acceptUnvalidated) {
+ return;
+ }
+
+ // TODO: What should we do if we've already switched to this network because we had no
+ // better option? There are two obvious alternatives.
+ //
+ // 1. Decide that there's no point prompting because this is our only usable network.
+ // However, because we didn't prompt, if later on a validated network comes along, we'll
+ // either a) silently switch to it - bad if the user wanted to connect to stay on this
+ // unvalidated network - or b) prompt the user at that later time - bad because the user
+ // might not understand why they are now being prompted.
+ //
+ // 2. Always prompt the user, even if we have no other network to use. The user could then
+ // try to find an alternative network to join (remember, if we got here, then the user
+ // selected this network manually). This is bad because the prompt isn't really very
+ // useful.
+ //
+ // For now we do #1, but we can revisit that later.
+ if (isDefaultNetwork(nai)) {
+ return;
+ }
+
+ Intent intent = new Intent(ConnectivityManager.ACTION_PROMPT_UNVALIDATED);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK, network);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setClassName("com.android.settings",
+ "com.android.settings.wifi.WifiNoInternetDialog");
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ }
+
private class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
@@ -2297,6 +2432,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
handleReleaseNetworkRequest((NetworkRequest) msg.obj, msg.arg1);
break;
}
+ case EVENT_SET_ACCEPT_UNVALIDATED: {
+ handleSetAcceptUnvalidated((Network) msg.obj, msg.arg1 != 0, msg.arg2 != 0);
+ break;
+ }
+ case EVENT_PROMPT_UNVALIDATED: {
+ handlePromptUnvalidated((Network) msg.obj);
+ break;
+ }
case EVENT_SYSTEM_READY: {
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
nai.networkMonitor.systemReady = true;
@@ -2435,25 +2578,27 @@ public class ConnectivityService extends IConnectivityManager.Stub
public void reportInetCondition(int networkType, int percentage) {
NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
if (nai == null) return;
- boolean isGood = percentage > 50;
- // Revalidate if the app report does not match our current validated state.
- if (isGood != nai.lastValidated) {
- // Make the message logged by reportBadNetwork below less confusing.
- if (DBG && isGood) log("reportInetCondition: type=" + networkType + " ok, revalidate");
- reportBadNetwork(nai.network);
- }
+ reportNetworkConnectivity(nai.network, percentage > 50);
}
- public void reportBadNetwork(Network network) {
+ public void reportNetworkConnectivity(Network network, boolean hasConnectivity) {
enforceAccessPermission();
enforceInternetPermission();
- if (network == null) return;
-
- final int uid = Binder.getCallingUid();
- NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+ NetworkAgentInfo nai;
+ if (network == null) {
+ nai = getDefaultNetwork();
+ } else {
+ nai = getNetworkAgentInfoForNetwork(network);
+ }
if (nai == null) return;
- if (DBG) log("reportBadNetwork(" + nai.name() + ") by " + uid);
+ // Revalidate if the app report does not match our current validated state.
+ if (hasConnectivity == nai.lastValidated) return;
+ final int uid = Binder.getCallingUid();
+ if (DBG) {
+ log("reportNetworkConnectivity(" + nai.network.netId + ", " + hasConnectivity +
+ ") by " + uid);
+ }
synchronized (nai) {
// Validating an uncreated network could result in a call to rematchNetworkAndRequests()
// which isn't meant to work on uncreated networks.
@@ -2912,23 +3057,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- public int findConnectionTypeForIface(String iface) {
- enforceConnectivityInternalPermission();
-
- if (TextUtils.isEmpty(iface)) return ConnectivityManager.TYPE_NONE;
-
- synchronized(mNetworkForNetId) {
- for (int i = 0; i < mNetworkForNetId.size(); i++) {
- NetworkAgentInfo nai = mNetworkForNetId.valueAt(i);
- LinkProperties lp = nai.linkProperties;
- if (lp != null && iface.equals(lp.getInterfaceName()) && nai.networkInfo != null) {
- return nai.networkInfo.getType();
- }
- }
- }
- return ConnectivityManager.TYPE_NONE;
- }
-
@Override
public int checkMobileProvisioning(int suggestedTimeOutMs) {
// TODO: Remove? Any reason to trigger a provisioning check?
@@ -3182,7 +3310,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
loge("Starting user already has a VPN");
return;
}
- userVpn = new Vpn(mHandler.getLooper(), mContext, mNetd, this, userId);
+ userVpn = new Vpn(mHandler.getLooper(), mContext, mNetd, userId);
mVpns.put(userId, userVpn);
}
}
@@ -3327,6 +3455,24 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
+ @Override
+ public boolean requestBwUpdate(Network network) {
+ enforceAccessPermission();
+ NetworkAgentInfo nai = null;
+ if (network == null) {
+ return false;
+ }
+ synchronized (mNetworkForNetId) {
+ nai = mNetworkForNetId.get(network.netId);
+ }
+ if (nai != null) {
+ nai.asyncChannel.sendMessage(android.net.NetworkAgent.CMD_REQUEST_BANDWIDTH_UPDATE);
+ return true;
+ }
+ return false;
+ }
+
+
private void enforceMeteredApnPolicy(NetworkCapabilities networkCapabilities) {
// if UID is restricted, don't allow them to bring up metered APNs
if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
@@ -3434,14 +3580,23 @@ public class ConnectivityService extends IConnectivityManager.Stub
* and the are the highest scored network available.
* the are keyed off the Requests requestId.
*/
+ // TODO: Yikes, this is accessed on multiple threads: add synchronization.
private final SparseArray<NetworkAgentInfo> mNetworkForRequestId =
new SparseArray<NetworkAgentInfo>();
+ // NOTE: Accessed on multiple threads, must be synchronized on itself.
+ @GuardedBy("mNetworkForNetId")
private final SparseArray<NetworkAgentInfo> mNetworkForNetId =
new SparseArray<NetworkAgentInfo>();
+ // NOTE: Accessed on multiple threads, synchronized with mNetworkForNetId.
+ // An entry is first added to mNetIdInUse, prior to mNetworkForNetId, so
+ // there may not be a strict 1:1 correlation between the two.
+ @GuardedBy("mNetworkForNetId")
+ private final SparseBooleanArray mNetIdInUse = new SparseBooleanArray();
// NetworkAgentInfo keyed off its connecting messenger
// TODO - eval if we can reduce the number of lists/hashmaps/sparsearrays
+ // NOTE: Only should be accessed on ConnectivityServiceThread, except dump().
private final HashMap<Messenger, NetworkAgentInfo> mNetworkAgentInfos =
new HashMap<Messenger, NetworkAgentInfo>();
@@ -3456,7 +3611,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
return nai == getDefaultNetwork();
}
- public void registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
+ public int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
int currentScore, NetworkMisc networkMisc) {
enforceConnectivityInternalPermission();
@@ -3464,20 +3619,23 @@ public class ConnectivityService extends IConnectivityManager.Stub
// TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
// satisfies mDefaultRequest.
NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
- new NetworkInfo(networkInfo), new LinkProperties(linkProperties),
- new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler,
- new NetworkMisc(networkMisc), mDefaultRequest);
+ new Network(reserveNetId()), new NetworkInfo(networkInfo), new LinkProperties(
+ linkProperties), new NetworkCapabilities(networkCapabilities), currentScore,
+ mContext, mTrackerHandler, new NetworkMisc(networkMisc), mDefaultRequest);
synchronized (this) {
nai.networkMonitor.systemReady = mSystemReady;
}
if (DBG) log("registerNetworkAgent " + nai);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai));
+ return nai.network.netId;
}
private void handleRegisterNetworkAgent(NetworkAgentInfo na) {
if (VDBG) log("Got NetworkAgent Messenger");
mNetworkAgentInfos.put(na.messenger, na);
- assignNextNetId(na);
+ synchronized (mNetworkForNetId) {
+ mNetworkForNetId.put(na.network.netId, na);
+ }
na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger);
NetworkInfo networkInfo = na.networkInfo;
na.networkInfo = null;
@@ -4097,8 +4255,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
networkAgent.created = true;
updateLinkProperties(networkAgent, null);
notifyIfacesChanged();
- notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
+
networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
+ scheduleUnvalidatedPrompt(networkAgent);
+
if (networkAgent.isVPN()) {
// Temporarily disable the default proxy (not global).
synchronized (mProxyLock) {
@@ -4111,9 +4271,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
// TODO: support proxy per network.
}
+
// Consider network even though it is not yet validated.
rematchNetworkAndRequests(networkAgent, NascentState.NOT_JUST_VALIDATED,
ReapUnvalidatedNetworks.REAP);
+
+ // This has to happen after matching the requests, because callbacks are just requests.
+ notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
} else if (state == NetworkInfo.State.DISCONNECTED ||
state == NetworkInfo.State.SUSPENDED) {
networkAgent.asyncChannel.disconnect();
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index a99f387..4c937f7 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -16,6 +16,13 @@
package com.android.server;
+import static com.android.internal.util.XmlUtils.readIntAttribute;
+import static com.android.internal.util.XmlUtils.readStringAttribute;
+import static com.android.internal.util.XmlUtils.writeIntAttribute;
+import static com.android.internal.util.XmlUtils.writeStringAttribute;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
import android.Manifest;
import android.app.ActivityManagerNative;
import android.app.AppOpsManager;
@@ -55,9 +62,13 @@ import android.os.storage.StorageVolume;
import android.os.storage.VolumeInfo;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.AtomicFile;
+import android.util.DebugUtils;
import android.util.Log;
import android.util.Slog;
+import android.util.Xml;
+import libcore.io.IoUtils;
import libcore.util.EmptyArray;
import libcore.util.HexEncoding;
@@ -66,14 +77,21 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IMediaContainerService;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.NativeDaemonConnector.Command;
import com.android.server.NativeDaemonConnector.SensitiveArg;
import com.android.server.pm.PackageManagerService;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
@@ -214,6 +232,57 @@ class MountService extends IMountService.Stub
public static final int FstrimCompleted = 700;
}
+ private static final String TAG_VOLUMES = "volumes";
+ private static final String TAG_VOLUME = "volume";
+ private static final String ATTR_TYPE = "type";
+ private static final String ATTR_FS_UUID = "fsUuid";
+ private static final String ATTR_NICKNAME = "nickname";
+ private static final String ATTR_USER_FLAGS = "userFlags";
+
+ private final AtomicFile mMetadataFile;
+
+ private static class VolumeMetadata {
+ public final int type;
+ public final String fsUuid;
+ public String nickname;
+ public int userFlags;
+
+ public VolumeMetadata(int type, String fsUuid) {
+ this.type = type;
+ this.fsUuid = Preconditions.checkNotNull(fsUuid);
+ }
+
+ public static VolumeMetadata read(XmlPullParser in) throws IOException {
+ final int type = readIntAttribute(in, ATTR_TYPE);
+ final String fsUuid = readStringAttribute(in, ATTR_FS_UUID);
+ final VolumeMetadata meta = new VolumeMetadata(type, fsUuid);
+ meta.nickname = readStringAttribute(in, ATTR_NICKNAME);
+ meta.userFlags = readIntAttribute(in, ATTR_USER_FLAGS);
+ return meta;
+ }
+
+ public static void write(XmlSerializer out, VolumeMetadata meta) throws IOException {
+ out.startTag(null, TAG_VOLUME);
+ writeIntAttribute(out, ATTR_TYPE, meta.type);
+ writeStringAttribute(out, ATTR_FS_UUID, meta.fsUuid);
+ writeStringAttribute(out, ATTR_NICKNAME, meta.nickname);
+ writeIntAttribute(out, ATTR_USER_FLAGS, meta.userFlags);
+ out.endTag(null, TAG_VOLUME);
+ }
+
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("VolumeMetadata:");
+ pw.increaseIndent();
+ pw.printPair("type", DebugUtils.valueToString(VolumeInfo.class, "TYPE_", type));
+ pw.printPair("fsUuid", fsUuid);
+ pw.printPair("nickname", nickname);
+ pw.printPair("userFlags",
+ DebugUtils.flagsToString(VolumeInfo.class, "USER_FLAG_", userFlags));
+ pw.decreaseIndent();
+ pw.println();
+ }
+ }
+
/**
* <em>Never</em> hold the lock while performing downcalls into vold, since
* unsolicited events can suddenly appear to update data structures.
@@ -222,11 +291,18 @@ class MountService extends IMountService.Stub
@GuardedBy("mLock")
private int[] mStartedUsers = EmptyArray.INT;
+
+ /** Map from disk ID to disk */
@GuardedBy("mLock")
private ArrayMap<String, DiskInfo> mDisks = new ArrayMap<>();
+ /** Map from volume ID to disk */
@GuardedBy("mLock")
private ArrayMap<String, VolumeInfo> mVolumes = new ArrayMap<>();
+ /** Map from UUID to metadata */
+ @GuardedBy("mLock")
+ private ArrayMap<String, VolumeMetadata> mMetadata = new ArrayMap<>();
+
private DiskInfo findDiskById(String id) {
synchronized (mLock) {
final DiskInfo disk = mDisks.get(id);
@@ -260,6 +336,15 @@ class MountService extends IMountService.Stub
throw new IllegalArgumentException("No volume found for path " + path);
}
+ private VolumeMetadata findOrCreateMetadataLocked(VolumeInfo vol) {
+ VolumeMetadata meta = mMetadata.get(vol.fsUuid);
+ if (meta == null) {
+ meta = new VolumeMetadata(vol.type, vol.fsUuid);
+ mMetadata.put(meta.fsUuid, meta);
+ }
+ return meta;
+ }
+
private static int sNextMtpIndex = 1;
private static int allocateMtpIndex(String volId) {
@@ -799,6 +884,7 @@ class MountService extends IMountService.Stub
if (vol != null) {
vol.fsType = cooked[2];
}
+ mCallbacks.notifyVolumeMetadataChanged(vol.clone());
break;
}
case VoldResponseCode.VOLUME_FS_UUID_CHANGED: {
@@ -807,6 +893,8 @@ class MountService extends IMountService.Stub
if (vol != null) {
vol.fsUuid = cooked[2];
}
+ refreshMetadataLocked();
+ mCallbacks.notifyVolumeMetadataChanged(vol.clone());
break;
}
case VoldResponseCode.VOLUME_FS_LABEL_CHANGED: {
@@ -815,6 +903,7 @@ class MountService extends IMountService.Stub
if (vol != null) {
vol.fsLabel = cooked[2];
}
+ mCallbacks.notifyVolumeMetadataChanged(vol.clone());
break;
}
case VoldResponseCode.VOLUME_PATH_CHANGED: {
@@ -901,6 +990,25 @@ class MountService extends IMountService.Stub
}
}
+ /**
+ * Refresh latest metadata into any currently active {@link VolumeInfo}.
+ */
+ private void refreshMetadataLocked() {
+ final int size = mVolumes.size();
+ for (int i = 0; i < size; i++) {
+ final VolumeInfo vol = mVolumes.valueAt(i);
+ final VolumeMetadata meta = mMetadata.get(vol.fsUuid);
+
+ if (meta != null) {
+ vol.nickname = meta.nickname;
+ vol.userFlags = meta.userFlags;
+ } else {
+ vol.nickname = null;
+ vol.userFlags = 0;
+ }
+ }
+ }
+
private void enforcePermission(String perm) {
mContext.enforceCallingOrSelfPermission(perm, perm);
}
@@ -949,6 +1057,13 @@ class MountService extends IMountService.Stub
mLastMaintenance = mLastMaintenanceFile.lastModified();
}
+ mMetadataFile = new AtomicFile(
+ new File(Environment.getSystemSecureDirectory(), "storage.xml"));
+
+ synchronized (mLock) {
+ readMetadataLocked();
+ }
+
/*
* Create the connection to vold with a maximum queue of twice the
* amount of containers we'd ever expect to have. This keeps an
@@ -972,6 +1087,61 @@ class MountService extends IMountService.Stub
mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
}
+ private void readMetadataLocked() {
+ mMetadata.clear();
+
+ FileInputStream fis = null;
+ try {
+ fis = mMetadataFile.openRead();
+ final XmlPullParser in = Xml.newPullParser();
+ in.setInput(fis, null);
+
+ int type;
+ while ((type = in.next()) != END_DOCUMENT) {
+ if (type == START_TAG) {
+ final String tag = in.getName();
+ if (TAG_VOLUME.equals(tag)) {
+ final VolumeMetadata meta = VolumeMetadata.read(in);
+ mMetadata.put(meta.fsUuid, meta);
+ }
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // Missing metadata is okay, probably first boot
+ } catch (IOException e) {
+ Slog.wtf(TAG, "Failed reading metadata", e);
+ } catch (XmlPullParserException e) {
+ Slog.wtf(TAG, "Failed reading metadata", e);
+ } finally {
+ IoUtils.closeQuietly(fis);
+ }
+ }
+
+ private void writeMetadataLocked() {
+ FileOutputStream fos = null;
+ try {
+ fos = mMetadataFile.startWrite();
+
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(fos, "utf-8");
+ out.startDocument(null, true);
+ out.startTag(null, TAG_VOLUMES);
+ final int size = mMetadata.size();
+ for (int i = 0; i < size; i++) {
+ final VolumeMetadata meta = mMetadata.valueAt(i);
+ VolumeMetadata.write(out, meta);
+ }
+ out.endTag(null, TAG_VOLUMES);
+ out.endDocument();
+
+ mMetadataFile.finishWrite(fos);
+ } catch (IOException e) {
+ if (fos != null) {
+ mMetadataFile.failWrite(fos);
+ }
+ }
+ }
+
/**
* Exposed API calls below here
*/
@@ -1126,6 +1296,36 @@ class MountService extends IMountService.Stub
}
@Override
+ public void setVolumeNickname(String volId, String nickname) {
+ enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+ waitForReady();
+
+ synchronized (mLock) {
+ final VolumeInfo vol = findVolumeById(volId);
+ final VolumeMetadata meta = findOrCreateMetadataLocked(vol);
+ meta.nickname = nickname;
+ refreshMetadataLocked();
+ writeMetadataLocked();
+ mCallbacks.notifyVolumeMetadataChanged(vol.clone());
+ }
+ }
+
+ @Override
+ public void setVolumeUserFlags(String volId, int flags, int mask) {
+ enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+ waitForReady();
+
+ synchronized (mLock) {
+ final VolumeInfo vol = findVolumeById(volId);
+ final VolumeMetadata meta = findOrCreateMetadataLocked(vol);
+ meta.userFlags = (meta.userFlags & ~mask) | (flags & mask);
+ refreshMetadataLocked();
+ writeMetadataLocked();
+ mCallbacks.notifyVolumeMetadataChanged(vol.clone());
+ }
+ }
+
+ @Override
public int[] getStorageUsers(String path) {
enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
waitForReady();
@@ -1909,7 +2109,12 @@ class MountService extends IMountService.Stub
}
@Override
- public VolumeInfo[] getVolumes() {
+ public VolumeInfo[] getVolumes(int flags) {
+ if ((flags & StorageManager.FLAG_ALL_METADATA) != 0) {
+ // TODO: implement support for returning all metadata
+ throw new UnsupportedOperationException();
+ }
+
synchronized (mLock) {
final VolumeInfo[] res = new VolumeInfo[mVolumes.size()];
for (int i = 0; i < mVolumes.size(); i++) {
@@ -2422,6 +2627,7 @@ class MountService extends IMountService.Stub
private static class Callbacks extends Handler {
private static final int MSG_STORAGE_STATE_CHANGED = 1;
private static final int MSG_VOLUME_STATE_CHANGED = 2;
+ private static final int MSG_VOLUME_METADATA_CHANGED = 3;
private final RemoteCallbackList<IMountServiceListener>
mCallbacks = new RemoteCallbackList<>();
@@ -2465,6 +2671,10 @@ class MountService extends IMountService.Stub
callback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
break;
}
+ case MSG_VOLUME_METADATA_CHANGED: {
+ callback.onVolumeMetadataChanged((VolumeInfo) args.arg1);
+ break;
+ }
}
}
@@ -2483,6 +2693,12 @@ class MountService extends IMountService.Stub
args.argi3 = newState;
obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
}
+
+ private void notifyVolumeMetadataChanged(VolumeInfo vol) {
+ final SomeArgs args = SomeArgs.obtain();
+ args.arg1 = vol;
+ obtainMessage(MSG_VOLUME_METADATA_CHANGED, args).sendToTarget();
+ }
}
@Override
@@ -2539,6 +2755,15 @@ class MountService extends IMountService.Stub
vol.dump(pw);
}
pw.decreaseIndent();
+
+ pw.println();
+ pw.println("Metadata:");
+ pw.increaseIndent();
+ for (int i = 0; i < mMetadata.size(); i++) {
+ final VolumeMetadata meta = mMetadata.valueAt(i);
+ meta.dump(pw);
+ }
+ pw.decreaseIndent();
}
pw.println();
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index d153233..908ee22 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -181,6 +181,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
private PreciseCallState mPreciseCallState = new PreciseCallState();
+ private boolean mCarrierNetworkChangeState = false;
+
private PreciseDataConnectionState mPreciseDataConnectionState =
new PreciseDataConnectionState();
@@ -607,6 +609,13 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
remove(r.binder);
}
}
+ if ((events & PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) != 0) {
+ try {
+ r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
}
}
} else {
@@ -790,6 +799,31 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
broadcastSignalStrengthChanged(signalStrength, subId);
}
+ @Override
+ public void notifyCarrierNetworkChange(boolean active) {
+ if (!checkNotifyPermissionOrCarrierPrivilege("notifyCarrierNetworkChange()")) {
+ return;
+ }
+ if (VDBG) {
+ log("notifyCarrierNetworkChange: active=" + active);
+ }
+
+ synchronized (mRecords) {
+ mCarrierNetworkChangeState = active;
+ for (Record r : mRecords) {
+ if (r.matchPhoneStateListenerEvent(
+ PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE)) {
+ try {
+ r.callback.onCarrierNetworkChange(active);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
public void notifyCellInfo(List<CellInfo> cellInfo) {
notifyCellInfoForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, cellInfo);
}
@@ -1422,9 +1456,19 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
android.Manifest.permission.READ_PRECISE_PHONE_STATE);
}
+ private boolean checkNotifyPermissionOrCarrierPrivilege(String method) {
+ if (checkNotifyPermission() || checkCarrierPrivilege()) {
+ return true;
+ }
+
+ String msg = "Modify Phone State or Carrier Privilege Permission Denial: " + method
+ + " from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid();
+ if (DBG) log(msg);
+ return false;
+ }
+
private boolean checkNotifyPermission(String method) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- == PackageManager.PERMISSION_GRANTED) {
+ if (checkNotifyPermission()) {
return true;
}
String msg = "Modify Phone State Permission Denial: " + method + " from pid="
@@ -1433,6 +1477,24 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
return false;
}
+ private boolean checkNotifyPermission() {
+ return mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private boolean checkCarrierPrivilege() {
+ TelephonyManager tm = TelephonyManager.getDefault();
+ String[] pkgs = mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid());
+ for (String pkg : pkgs) {
+ if (tm.checkCarrierPrivilegesForPackage(pkg) ==
+ TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
private void checkListenerPermission(int events) {
if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
mContext.enforceCallingOrSelfPermission(
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 794e1b0..772a15c 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -46,7 +46,6 @@ import java.util.ArrayList;
/** This class calls its monitor every minute. Killing this process if they don't return **/
public class Watchdog extends Thread {
static final String TAG = "Watchdog";
- static final boolean localLOGV = false || false;
// Set this to true to use debug default values.
static final boolean DB = false;
@@ -73,7 +72,7 @@ public class Watchdog extends Thread {
static Watchdog sWatchdog;
/* This handler will be used to post message back onto the main thread */
- final ArrayList<HandlerChecker> mHandlerCheckers = new ArrayList<HandlerChecker>();
+ final ArrayList<HandlerChecker> mHandlerCheckers = new ArrayList<>();
final HandlerChecker mMonitorChecker;
ContentResolver mResolver;
ActivityManagerService mActivity;
@@ -191,6 +190,17 @@ public class Watchdog extends Thread {
}
}
+ /** Monitor for checking the availability of binder threads. The monitor will block until
+ * there is a binder thread available to process in coming IPCs to make sure other processes
+ * can still communicate with the service.
+ */
+ private static final class BinderThreadMonitor implements Watchdog.Monitor {
+ @Override
+ public void monitor() {
+ Binder.blockUntilThreadAvailable();
+ }
+ }
+
public interface Monitor {
void monitor();
}
@@ -228,6 +238,9 @@ public class Watchdog extends Thread {
// And the display thread.
mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(),
"display thread", DEFAULT_TIMEOUT));
+
+ // Initialize monitor for Binder threads.
+ addMonitor(new BinderThreadMonitor());
}
public void init(Context context, ActivityManagerService activity) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b606353..b658932 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -26,10 +26,14 @@ import static com.android.internal.util.XmlUtils.readLongAttribute;
import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
-import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
+import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
import static com.android.server.am.ActivityManagerDebugConfig.*;
+import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
-import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -3982,13 +3986,13 @@ public final class ActivityManagerService extends ActivityManagerNative
if (rootR == null) {
Slog.w(TAG, "Finishing task with all activities already finished");
}
- // Do not allow task to finish in Lock Task mode.
- if (tr == mStackSupervisor.mLockTaskModeTask) {
- if (rootR == r) {
- Slog.i(TAG, "Not finishing task in lock task mode");
- mStackSupervisor.showLockTaskToast();
- return false;
- }
+ // Do not allow task to finish if last task in lockTask mode. Launchable apps can
+ // finish themselves.
+ if (tr.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE && rootR == r &&
+ mStackSupervisor.isLastLockedTask(tr)) {
+ Slog.i(TAG, "Not finishing task in lock task mode");
+ mStackSupervisor.showLockTaskToast();
+ return false;
}
if (mController != null) {
// Find the first activity that is not finishing.
@@ -4142,20 +4146,18 @@ public final class ActivityManagerService extends ActivityManagerNative
final long origId = Binder.clearCallingIdentity();
try {
ActivityRecord r = ActivityRecord.isInStackLocked(token);
-
- ActivityRecord rootR = r.task.getRootActivity();
- // Do not allow task to finish in Lock Task mode.
- if (r.task == mStackSupervisor.mLockTaskModeTask) {
- if (rootR == r) {
- mStackSupervisor.showLockTaskToast();
- return false;
- }
+ if (r == null) {
+ return false;
}
- boolean res = false;
- if (r != null) {
- res = r.task.stack.finishActivityAffinityLocked(r);
+
+ // Do not allow the last non-launchable task to finish in Lock Task mode.
+ final TaskRecord task = r.task;
+ if (task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE &&
+ mStackSupervisor.isLastLockedTask(task) && task.getRootActivity() == r) {
+ mStackSupervisor.showLockTaskToast();
+ return false;
}
- return res;
+ return task.stack.finishActivityAffinityLocked(r);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -7849,6 +7851,27 @@ public final class ActivityManagerService extends ActivityManagerNative
rti.lastActiveTime = tr.lastActiveTime;
rti.affiliatedTaskId = tr.mAffiliatedTaskId;
rti.affiliatedTaskColor = tr.mAffiliatedTaskColor;
+ rti.numActivities = 0;
+
+ ActivityRecord base = null;
+ ActivityRecord top = null;
+ ActivityRecord tmp;
+
+ for (int i = tr.mActivities.size() - 1; i >= 0; --i) {
+ tmp = tr.mActivities.get(i);
+ if (tmp.finishing) {
+ continue;
+ }
+ base = tmp;
+ if (top == null || (top.state == ActivityState.INITIALIZING)) {
+ top = base;
+ }
+ rti.numActivities++;
+ }
+
+ rti.baseActivity = (base != null) ? base.intent.getComponent() : null;
+ rti.topActivity = (top != null) ? top.intent.getComponent() : null;
+
return rti;
}
@@ -8336,9 +8359,9 @@ public final class ActivityManagerService extends ActivityManagerNative
final long origId = Binder.clearCallingIdentity();
try {
int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot);
- if (taskId >= 0) {
- if ((mStackSupervisor.mLockTaskModeTask != null)
- && (mStackSupervisor.mLockTaskModeTask.taskId == taskId)) {
+ final TaskRecord task = mRecentTasks.taskForIdLocked(taskId);
+ if (task != null) {
+ if (mStackSupervisor.isLockedTask(task)) {
mStackSupervisor.showLockTaskToast();
return false;
}
@@ -8520,47 +8543,45 @@ public final class ActivityManagerService extends ActivityManagerNative
@Override
public void updateLockTaskPackages(int userId, String[] packages) {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
throw new SecurityException("updateLockTaskPackage called from non-system process");
}
synchronized (this) {
mLockTaskPackages.put(userId, packages);
+ mStackSupervisor.onLockTaskPackagesUpdatedLocked();
}
}
- private boolean isLockTaskAuthorizedLocked(String pkg) {
- String[] packages = mLockTaskPackages.get(mCurrentUserId);
- if (packages == null) {
- return false;
- }
- for (int i = packages.length - 1; i >= 0; --i) {
- if (pkg.equals(packages[i])) {
- return true;
- }
- }
- return false;
- }
void startLockTaskModeLocked(TaskRecord task) {
- final String pkg = task.intent.getComponent().getPackageName();
+ if (task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
+ return;
+ }
+
// isSystemInitiated is used to distinguish between locked and pinned mode, as pinned mode
// is initiated by system after the pinning request was shown and locked mode is initiated
// by an authorized app directly
- boolean isSystemInitiated = Binder.getCallingUid() == Process.SYSTEM_UID;
+ final int callingUid = Binder.getCallingUid();
+ boolean isSystemInitiated = callingUid == Process.SYSTEM_UID;
long ident = Binder.clearCallingIdentity();
try {
- if (!isSystemInitiated && !isLockTaskAuthorizedLocked(pkg)) {
- StatusBarManagerInternal statusBarManager =
- LocalServices.getService(StatusBarManagerInternal.class);
- if (statusBarManager != null) {
- statusBarManager.showScreenPinningRequest();
+ final ActivityStack stack = mStackSupervisor.getFocusedStack();
+ if (!isSystemInitiated) {
+ task.mLockTaskUid = callingUid;
+ if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) {
+ // startLockTask() called by app and task mode is lockTaskModeDefault.
+ StatusBarManagerInternal statusBarManager =
+ LocalServices.getService(StatusBarManagerInternal.class);
+ if (statusBarManager != null) {
+ statusBarManager.showScreenPinningRequest();
+ }
+ return;
}
- return;
- }
- final ActivityStack stack = mStackSupervisor.getFocusedStack();
- if (!isSystemInitiated && (stack == null || task != stack.topTask())) {
- throw new IllegalArgumentException("Invalid task, not in foreground");
+ if (stack == null || task != stack.topTask()) {
+ throw new IllegalArgumentException("Invalid task, not in foreground");
+ }
}
mStackSupervisor.setLockTaskModeLocked(task, isSystemInitiated ?
ActivityManager.LOCK_TASK_MODE_PINNED :
@@ -8614,23 +8635,15 @@ public final class ActivityManagerService extends ActivityManagerNative
@Override
public void stopLockTaskMode() {
- // Verify that the user matches the package of the intent for the TaskRecord
- // we are locked to or systtem. This will ensure the same caller for startLockTaskMode
- // and stopLockTaskMode.
- final int callingUid = Binder.getCallingUid();
- if (callingUid != Process.SYSTEM_UID) {
- try {
- String pkg =
- mStackSupervisor.mLockTaskModeTask.intent.getComponent().getPackageName();
- int uid = mContext.getPackageManager().getPackageUid(pkg,
- Binder.getCallingUserHandle().getIdentifier());
- if (uid != callingUid) {
- throw new SecurityException("Invalid uid, expected " + uid);
- }
- } catch (NameNotFoundException e) {
- Log.d(TAG, "stopLockTaskMode " + e);
- return;
- }
+ final TaskRecord lockTask = mStackSupervisor.getLockedTaskLocked();
+ if (lockTask == null) {
+ // Our work here is done.
+ return;
+ }
+ // Ensure the same caller for startLockTaskMode and stopLockTaskMode.
+ if (getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_LOCKED &&
+ Binder.getCallingUid() != lockTask.mLockTaskUid) {
+ throw new SecurityException("Invalid uid, expected " + lockTask.mLockTaskUid);
}
long ident = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 2362d28..5b1543e 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -16,7 +16,7 @@
package com.android.server.am;
-import static android.content.pm.ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN;
+import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static com.android.server.am.ActivityManagerDebugConfig.*;
@@ -375,7 +375,7 @@ final class ActivityStack {
}
boolean okToShowLocked(ActivityRecord r) {
- return isCurrentProfileLocked(r.userId) || (r.info.flags & FLAG_SHOW_ON_LOCK_SCREEN) != 0;
+ return isCurrentProfileLocked(r.userId) || (r.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0;
}
final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
@@ -624,7 +624,7 @@ final class ActivityStack {
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord r = activities.get(activityNdx);
- if (notCurrentUserTask && (r.info.flags & FLAG_SHOW_ON_LOCK_SCREEN) == 0) {
+ if (notCurrentUserTask && (r.info.flags & FLAG_SHOW_FOR_ALL_USERS) == 0) {
return null;
}
if (!r.finishing && r.intent.getComponent().equals(cls) && r.userId == userId) {
@@ -2024,7 +2024,7 @@ final class ActivityStack {
// Now put task at top.
int taskNdx = mTaskHistory.size();
final boolean notShownWhenLocked =
- (newActivity != null && (newActivity.info.flags & FLAG_SHOW_ON_LOCK_SCREEN) == 0)
+ (newActivity != null && (newActivity.info.flags & FLAG_SHOW_FOR_ALL_USERS) == 0)
|| (newActivity == null && task.topRunningActivityLocked(null) == null);
if (!isCurrentProfileLocked(task.userId) && notShownWhenLocked) {
// Put non-current user tasks below current user tasks.
@@ -2074,7 +2074,7 @@ final class ActivityStack {
r.putInHistory();
mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
- (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0,
+ (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0,
r.userId, r.info.configChanges, task.voiceSession != null,
r.mLaunchTaskBehind);
if (VALIDATE_TOKENS) {
@@ -2138,7 +2138,7 @@ final class ActivityStack {
}
mWindowManager.addAppToken(task.mActivities.indexOf(r),
r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
- (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId,
+ (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId,
r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind);
boolean doShow = true;
if (newTask) {
@@ -2190,7 +2190,7 @@ final class ActivityStack {
// because there is nothing for it to animate on top of.
mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
- (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId,
+ (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId,
r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind);
ActivityOptions.abort(options);
options = null;
@@ -2875,7 +2875,7 @@ final class ActivityStack {
}
if (endTask) {
- mStackSupervisor.endLockTaskModeIfTaskEnding(task);
+ mStackSupervisor.removeLockedTaskLocked(task);
}
} else if (r.state != ActivityState.PAUSING) {
// If the activity is PAUSING, we will complete the finish once
@@ -3674,8 +3674,7 @@ final class ActivityStack {
}
Slog.i(TAG, "moveTaskToBack: " + tr);
-
- mStackSupervisor.endLockTaskModeIfTaskEnding(tr);
+ mStackSupervisor.removeLockedTaskLocked(tr);
// If we have a watcher, preflight the move before committing to it. First check
// for *other* available tasks, but if none are available, then try again allowing the
@@ -4062,6 +4061,7 @@ final class ActivityStack {
}
ActivityRecord r = null;
ActivityRecord top = null;
+ ActivityRecord tmp;
int numActivities = 0;
int numRunning = 0;
final ArrayList<ActivityRecord> activities = task.mActivities;
@@ -4069,7 +4069,11 @@ final class ActivityStack {
continue;
}
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- r = activities.get(activityNdx);
+ tmp = activities.get(activityNdx);
+ if (tmp.finishing) {
+ continue;
+ }
+ r = tmp;
// Initialize state for next task if needed.
if (top == null || (top.state == ActivityState.INITIALIZING)) {
@@ -4240,7 +4244,7 @@ final class ActivityStack {
*/
void removeTask(TaskRecord task, String reason, boolean notMoving) {
if (notMoving) {
- mStackSupervisor.endLockTaskModeIfTaskEnding(task);
+ mStackSupervisor.removeLockedTaskLocked(task);
mWindowManager.removeTask(task.taskId);
}
@@ -4345,4 +4349,10 @@ final class ActivityStack {
mFullscreen = Configuration.EMPTY.equals(mOverrideConfig);
return !mOverrideConfig.equals(oldConfig);
}
+
+ void onLockTaskPackagesUpdatedLocked() {
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ mTaskHistory.get(taskNdx).setLockTaskAuth();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index c2f6bfd..58bdc28 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -17,10 +17,14 @@
package com.android.server.am;
import static android.Manifest.permission.START_ANY_ACTIVITY;
+import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
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.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.server.am.ActivityManagerDebugConfig.*;
import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
@@ -28,6 +32,10 @@ import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
import static com.android.server.am.ActivityStack.ActivityState.*;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
import android.app.Activity;
import android.app.ActivityManager;
@@ -261,17 +269,17 @@ public final class ActivityStackSupervisor implements DisplayListener {
// TODO: Add listener for removal of references.
/** Mapping from (ActivityStack/TaskStack).mStackId to their current state */
- private SparseArray<ActivityContainer> mActivityContainers = new SparseArray<ActivityContainer>();
+ private SparseArray<ActivityContainer> mActivityContainers = new SparseArray<>();
/** Mapping from displayId to display current state */
- private final SparseArray<ActivityDisplay> mActivityDisplays =
- new SparseArray<ActivityDisplay>();
+ private final SparseArray<ActivityDisplay> mActivityDisplays = new SparseArray<>();
InputManagerInternal mInputManagerInternal;
- /** If non-null then the task specified remains in front and no other tasks may be started
- * until the task exits or #stopLockTaskMode() is called. */
- TaskRecord mLockTaskModeTask;
+ /** The chain of tasks in lockTask mode. The current frontmost task is at the top, and tasks
+ * may be finished until there is only one entry left. If this is empty the system is not
+ * in lockTask mode. */
+ ArrayList<TaskRecord> mLockTaskModeTasks = new ArrayList<>();
/** Store the current lock task mode. Possible values:
* {@link ActivityManager#LOCK_TASK_MODE_NONE}, {@link ActivityManager#LOCK_TASK_MODE_LOCKED},
* {@link ActivityManager#LOCK_TASK_MODE_PINNED}
@@ -282,8 +290,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
*/
private LockTaskNotify mLockTaskNotify;
- final ArrayList<PendingActivityLaunch> mPendingActivityLaunches
- = new ArrayList<PendingActivityLaunch>();
+ final ArrayList<PendingActivityLaunch> mPendingActivityLaunches = new ArrayList<>();
/** Used to keep resumeTopActivityLocked() from being entered recursively */
boolean inResumeTopActivity;
@@ -796,7 +803,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
- ArrayList<RunningTaskInfo> stackTaskList = new ArrayList<RunningTaskInfo>();
+ ArrayList<RunningTaskInfo> stackTaskList = new ArrayList<>();
runningTaskLists.add(stackTaskList);
stack.getTasksLocked(stackTaskList, callingUid, allowed);
}
@@ -894,8 +901,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
intent = new Intent(intent);
// Collect information about the target of the Intent.
- ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags,
- profilerInfo, userId);
+ ActivityInfo aInfo =
+ resolveActivity(intent, resolvedType, startFlags, profilerInfo, userId);
ActivityContainer container = (ActivityContainer)iContainer;
synchronized (mService) {
@@ -1170,7 +1177,12 @@ public final class ActivityStackSupervisor implements DisplayListener {
mService.updateLruProcessLocked(app, true, null);
mService.updateOomAdjLocked();
- final ActivityStack stack = r.task.stack;
+ final TaskRecord task = r.task;
+ if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) {
+ setLockTaskModeLocked(task, LOCK_TASK_MODE_LOCKED, "lockTaskLaunchMode attribute");
+ }
+
+ final ActivityStack stack = task.stack;
try {
if (app.thread == null) {
throw new RemoteException();
@@ -1187,11 +1199,11 @@ public final class ActivityStackSupervisor implements DisplayListener {
if (andResume) {
EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY,
r.userId, System.identityHashCode(r),
- r.task.taskId, r.shortComponentName);
+ task.taskId, r.shortComponentName);
}
if (r.isHomeActivity() && r.isNotResolverActivity()) {
// Home process is the root process of the task.
- mService.mHomeProcess = r.task.mActivities.get(0).app;
+ mService.mHomeProcess = task.mActivities.get(0).app;
}
mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
r.sleeping = false;
@@ -1233,7 +1245,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
new Configuration(stack.mOverrideConfig), r.compat, r.launchedFromPackage,
- r.task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
+ task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);
if ((app.info.privateFlags&ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
@@ -1946,7 +1958,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
intentActivity = targetStack.resetTaskIfNeededLocked(intentActivity, r);
}
- if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
+ if ((startFlags & ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
// We don't need to start a new activity, and
// the client said not to do anything if that
// is the case, so this is it! And for paranoia, make
@@ -1964,8 +1976,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
return ActivityManager.START_RETURN_INTENT_TO_CALLER;
}
- if ((launchFlags &
- (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
+ if ((launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
// The caller has requested to completely replace any
// existing task with its new activity. Well that should
@@ -2128,10 +2139,6 @@ public final class ActivityStackSupervisor implements DisplayListener {
// Should this be considered a new task?
if (r.resultTo == null && inTask == null && !addingToTask
&& (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
- if (isLockTaskModeViolation(reuseTask)) {
- Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
- return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
- }
newTask = true;
targetStack = computeStackFocus(r, newTask);
targetStack.moveToFront("startingNewTask");
@@ -2147,6 +2154,10 @@ public final class ActivityStackSupervisor implements DisplayListener {
} else {
r.setTask(reuseTask, taskToAffiliate);
}
+ if (isLockTaskModeViolation(r.task)) {
+ Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
+ return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
+ }
if (!movedHome) {
if ((launchFlags &
(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
@@ -2815,7 +2826,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
final ActivityRecord r = activities.get(activityNdx);
mWindowManager.addAppToken(0, r.appToken, task.taskId, stack.mStackId,
r.info.screenOrientation, r.fullscreen,
- (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0,
+ (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0,
r.userId, r.info.configChanges, task.voiceSession != null,
r.mLaunchTaskBehind);
}
@@ -3292,6 +3303,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
pw.print(prefix); pw.println("mCurTaskId=" + mCurTaskId);
pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
pw.print(prefix); pw.println("mActivityContainers=" + mActivityContainers);
+ pw.print(prefix); pw.println("mLockTaskModeTasks" + mLockTaskModeTasks);
}
ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
@@ -3592,6 +3604,32 @@ public final class ActivityStackSupervisor implements DisplayListener {
return list;
}
+ TaskRecord getLockedTaskLocked() {
+ final int top = mLockTaskModeTasks.size() - 1;
+ if (top >= 0) {
+ return mLockTaskModeTasks.get(top);
+ }
+ return null;
+ }
+
+ boolean isLockedTask(TaskRecord task) {
+ return mLockTaskModeTasks.contains(task);
+ }
+
+ boolean isLastLockedTask(TaskRecord task) {
+ return mLockTaskModeTasks.size() == 1 && mLockTaskModeTasks.contains(task);
+ }
+
+ void removeLockedTaskLocked(final TaskRecord task) {
+ if (mLockTaskModeTasks.remove(task) && mLockTaskModeTasks.isEmpty()) {
+ // Last one.
+ final Message lockTaskMsg = Message.obtain();
+ lockTaskMsg.arg1 = task.userId;
+ lockTaskMsg.what = LOCK_TASK_END_MSG;
+ mHandler.sendMessage(lockTaskMsg);
+ }
+ }
+
void showLockTaskToast() {
mLockTaskNotify.showToast(mLockTaskModeState);
}
@@ -3599,42 +3637,93 @@ public final class ActivityStackSupervisor implements DisplayListener {
void setLockTaskModeLocked(TaskRecord task, int lockTaskModeState, String reason) {
if (task == null) {
// Take out of lock task mode if necessary
- if (mLockTaskModeTask != null) {
- final Message lockTaskMsg = Message.obtain();
- lockTaskMsg.arg1 = mLockTaskModeTask.userId;
- lockTaskMsg.what = LOCK_TASK_END_MSG;
- mLockTaskModeTask = null;
- mHandler.sendMessage(lockTaskMsg);
+ final TaskRecord lockedTask = getLockedTaskLocked();
+ if (lockedTask != null) {
+ removeLockedTaskLocked(lockedTask);
+ if (!mLockTaskModeTasks.isEmpty()) {
+ // There are locked tasks remaining, can only finish this task, not unlock it.
+ lockedTask.performClearTaskLocked();
+ resumeTopActivitiesLocked();
+ return;
+ }
}
return;
}
+
+ // Should have already been checked, but do it again.
+ if (task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
+ return;
+ }
if (isLockTaskModeViolation(task)) {
- Slog.e(TAG, "setLockTaskMode: Attempt to start a second Lock Task Mode task.");
+ Slog.e(TAG, "setLockTaskMode: Attempt to start an unauthorized lock task.");
return;
}
- mLockTaskModeTask = task;
+
+ if (mLockTaskModeTasks.isEmpty()) {
+ // First locktask.
+ final Message lockTaskMsg = Message.obtain();
+ lockTaskMsg.obj = task.intent.getComponent().getPackageName();
+ lockTaskMsg.arg1 = task.userId;
+ lockTaskMsg.what = LOCK_TASK_START_MSG;
+ lockTaskMsg.arg2 = lockTaskModeState;
+ mHandler.sendMessage(lockTaskMsg);
+ }
+ // Add it or move it to the top.
+ mLockTaskModeTasks.remove(task);
+ mLockTaskModeTasks.add(task);
+
+ if (task.mLockTaskUid == -1) {
+ task.mLockTaskUid = task.mCallingUid;
+ }
findTaskToMoveToFrontLocked(task, 0, null, reason);
resumeTopActivitiesLocked();
-
- final Message lockTaskMsg = Message.obtain();
- lockTaskMsg.obj = mLockTaskModeTask.intent.getComponent().getPackageName();
- lockTaskMsg.arg1 = mLockTaskModeTask.userId;
- lockTaskMsg.what = LOCK_TASK_START_MSG;
- lockTaskMsg.arg2 = lockTaskModeState;
- mHandler.sendMessage(lockTaskMsg);
}
boolean isLockTaskModeViolation(TaskRecord task) {
- return mLockTaskModeTask != null && mLockTaskModeTask != task;
+ if (getLockedTaskLocked() == task) {
+ return false;
+ }
+ final int lockTaskAuth = task.mLockTaskAuth;
+ switch (lockTaskAuth) {
+ case LOCK_TASK_AUTH_DONT_LOCK:
+ return !mLockTaskModeTasks.isEmpty();
+ case LOCK_TASK_AUTH_LAUNCHABLE:
+ case LOCK_TASK_AUTH_WHITELISTED:
+ return false;
+ case LOCK_TASK_AUTH_PINNABLE:
+ // Pinnable tasks can't be launched on top of locktask tasks.
+ return !mLockTaskModeTasks.isEmpty();
+ default:
+ Slog.w(TAG, "isLockTaskModeViolation: invalid lockTaskAuth value=" + lockTaskAuth);
+ return true;
+ }
}
- void endLockTaskModeIfTaskEnding(TaskRecord task) {
- if (mLockTaskModeTask != null && mLockTaskModeTask == task) {
- final Message lockTaskMsg = Message.obtain();
- lockTaskMsg.arg1 = mLockTaskModeTask.userId;
- lockTaskMsg.what = LOCK_TASK_END_MSG;
- mLockTaskModeTask = null;
- mHandler.sendMessage(lockTaskMsg);
+ void onLockTaskPackagesUpdatedLocked() {
+ boolean didSomething = false;
+ for (int taskNdx = mLockTaskModeTasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ final TaskRecord lockedTask = mLockTaskModeTasks.get(taskNdx);
+ if (lockedTask.mLockTaskMode != LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED) {
+ continue;
+ }
+ final boolean wasLaunchable = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE;
+ lockedTask.setLockTaskAuth();
+ if (wasLaunchable && lockedTask.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE) {
+ // Lost whitelisting authorization. End it now.
+ removeLockedTaskLocked(lockedTask);
+ lockedTask.performClearTaskLocked();
+ didSomething = true;
+ }
+ }
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = stacks.get(stackNdx);
+ stack.onLockTaskPackagesUpdatedLocked();
+ }
+ }
+ if (didSomething) {
+ resumeTopActivitiesLocked();
}
}
@@ -3734,11 +3823,10 @@ public final class ActivityStackSupervisor implements DisplayListener {
mLockTaskModeState = msg.arg2;
if (getStatusBarService() != null) {
int flags = 0;
- if (mLockTaskModeState == ActivityManager.LOCK_TASK_MODE_LOCKED) {
+ if (mLockTaskModeState == LOCK_TASK_MODE_LOCKED) {
flags = StatusBarManager.DISABLE_MASK
& (~StatusBarManager.DISABLE_BACK);
- } else if (mLockTaskModeState ==
- ActivityManager.LOCK_TASK_MODE_PINNED) {
+ } else if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
flags = StatusBarManager.DISABLE_MASK
& (~StatusBarManager.DISABLE_BACK)
& (~StatusBarManager.DISABLE_HOME)
@@ -3776,8 +3864,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
boolean shouldLockKeyguard = Settings.Secure.getInt(
mService.mContext.getContentResolver(),
Settings.Secure.LOCK_TO_APP_EXIT_LOCKED) != 0;
- if (mLockTaskModeState == ActivityManager.LOCK_TASK_MODE_PINNED &&
- shouldLockKeyguard) {
+ if (mLockTaskModeState == LOCK_TASK_MODE_PINNED && shouldLockKeyguard) {
mWindowManager.lockNow(null);
mWindowManager.dismissKeyguard();
new LockPatternUtils(mService.mContext)
@@ -3789,7 +3876,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
} catch (RemoteException ex) {
throw new RuntimeException(ex);
} finally {
- mLockTaskModeState = ActivityManager.LOCK_TASK_MODE_NONE;
+ mLockTaskModeState = LOCK_TASK_MODE_NONE;
}
} break;
case CONTAINER_CALLBACK_TASK_LIST_EMPTY: {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 82e6d47..790a78d 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -18,6 +18,10 @@ package com.android.server.am;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
import static com.android.server.am.ActivityManagerDebugConfig.*;
import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
@@ -121,6 +125,20 @@ final class TaskRecord {
boolean mResizeable; // Activities in the task resizeable. Based on the resizable setting of
// the root activity.
+ int mLockTaskMode; // Which tasklock mode to launch this task in. One of
+ // ActivityManager.LOCK_TASK_LAUNCH_MODE_*
+ /** Can't be put in lockTask mode. */
+ final static int LOCK_TASK_AUTH_DONT_LOCK = 0;
+ /** Can enter lockTask with user approval if not already in lockTask. */
+ final static int LOCK_TASK_AUTH_PINNABLE = 1;
+ /** Starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing lockTask task. */
+ final static int LOCK_TASK_AUTH_LAUNCHABLE = 2;
+ /** Enters LOCK_TASK_MODE_LOCKED via startLockTask(), enters LOCK_TASK_MODE_PINNED from
+ * Overview. Can start over existing lockTask task. */
+ final static int LOCK_TASK_AUTH_WHITELISTED = 3;
+ int mLockTaskAuth = LOCK_TASK_AUTH_PINNABLE;
+
+ int mLockTaskUid = -1; // The uid of the application that called startLockTask().
// This represents the last resolved activity values for this task
// NOTE: This value needs to be persisted with each task
@@ -186,6 +204,8 @@ final class TaskRecord {
voiceInteractor = _voiceInteractor;
isAvailable = true;
mActivities = new ArrayList<>();
+ mCallingUid = info.applicationInfo.uid;
+ mCallingPackage = info.packageName;
setIntent(_intent, info);
}
@@ -201,12 +221,12 @@ final class TaskRecord {
voiceInteractor = null;
isAvailable = true;
mActivities = new ArrayList<>();
+ mCallingUid = info.applicationInfo.uid;
+ mCallingPackage = info.packageName;
setIntent(_intent, info);
taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
isPersistable = true;
- mCallingUid = info.applicationInfo.uid;
- mCallingPackage = info.packageName;
// Clamp to [1, max].
maxRecents = Math.min(Math.max(info.maxRecents, 1),
ActivityManager.getMaxAppRecentsLimitStatic());
@@ -215,8 +235,6 @@ final class TaskRecord {
mTaskToReturnTo = HOME_ACTIVITY_TYPE;
userId = UserHandle.getUserId(info.applicationInfo.uid);
lastTaskDescription = _taskDescription;
- mCallingUid = info.applicationInfo.uid;
- mCallingPackage = info.packageName;
}
private TaskRecord(ActivityManagerService service, int _taskId, Intent _intent,
@@ -278,9 +296,9 @@ final class TaskRecord {
/** Sets the original intent, and the calling uid and package. */
void setIntent(ActivityRecord r) {
- setIntent(r.intent, r.info);
mCallingUid = r.launchedFromUid;
mCallingPackage = r.launchedFromPackage;
+ setIntent(r.intent, r.info);
}
/** Sets the original intent, _without_ updating the calling uid or package. */
@@ -362,6 +380,8 @@ final class TaskRecord {
autoRemoveRecents = false;
}
mResizeable = info.resizeable;
+ mLockTaskMode = info.lockTaskLaunchMode;
+ setLockTaskAuth();
}
void setTaskToReturnTo(int taskToReturnTo) {
@@ -716,6 +736,53 @@ final class TaskRecord {
performClearTaskAtIndexLocked(0);
}
+ private boolean isPrivileged() {
+ final ProcessRecord proc = mService.mProcessNames.get(mCallingPackage, mCallingUid);
+ if (proc != null) {
+ return (proc.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
+ }
+ return false;
+ }
+
+ void setLockTaskAuth() {
+ switch (mLockTaskMode) {
+ case LOCK_TASK_LAUNCH_MODE_DEFAULT:
+ mLockTaskAuth = isLockTaskWhitelistedLocked() ?
+ LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE;
+ break;
+
+ case LOCK_TASK_LAUNCH_MODE_NEVER:
+ mLockTaskAuth = isPrivileged() ?
+ LOCK_TASK_AUTH_DONT_LOCK : LOCK_TASK_AUTH_PINNABLE;
+ break;
+
+ case LOCK_TASK_LAUNCH_MODE_ALWAYS:
+ mLockTaskAuth = isPrivileged() ?
+ LOCK_TASK_AUTH_LAUNCHABLE: LOCK_TASK_AUTH_PINNABLE;
+ break;
+
+ case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED:
+ mLockTaskAuth = isLockTaskWhitelistedLocked() ?
+ LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE;
+ break;
+ }
+ }
+
+ boolean isLockTaskWhitelistedLocked() {
+ if (mCallingPackage == null) {
+ return false;
+ }
+ String[] packages = mService.mLockTaskPackages.get(userId);
+ if (packages == null) {
+ return false;
+ }
+ for (int i = packages.length - 1; i >= 0; --i) {
+ if (mCallingPackage.equals(packages[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
boolean isHomeTask() {
return taskType == HOME_ACTIVITY_TYPE;
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index f3e0bbc..8a7c902 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -40,7 +40,10 @@ import java.util.ArrayList;
*/
public class NetworkAgentInfo {
public NetworkInfo networkInfo;
- public Network network;
+ // This Network object should always be used if possible, so as to encourage reuse of the
+ // enclosed socket factory and connection pool. Avoid creating other Network objects.
+ // This Network object is always valid.
+ public final Network network;
public LinkProperties linkProperties;
public NetworkCapabilities networkCapabilities;
public final NetworkMonitor networkMonitor;
@@ -68,7 +71,10 @@ public class NetworkAgentInfo {
private static final int UNVALIDATED_SCORE_PENALTY = 40;
// Score for explicitly connected network.
- private static final int EXPLICITLY_SELECTED_NETWORK_SCORE = 100;
+ //
+ // This ensures that a) the explicitly selected network is never trumped by anything else, and
+ // b) the explicitly selected network is never torn down.
+ private static final int MAXIMUM_NETWORK_SCORE = 100;
// The list of NetworkRequests being satisfied by this Network.
public final SparseArray<NetworkRequest> networkRequests = new SparseArray<NetworkRequest>();
@@ -83,12 +89,12 @@ public class NetworkAgentInfo {
// Used by ConnectivityService to keep track of 464xlat.
public Nat464Xlat clatd;
- public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, NetworkInfo info,
+ public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
NetworkMisc misc, NetworkRequest defaultRequest) {
this.messenger = messenger;
asyncChannel = ac;
- network = null;
+ network = net;
networkInfo = info;
linkProperties = lp;
networkCapabilities = nc;
@@ -120,13 +126,18 @@ public class NetworkAgentInfo {
// score. The NetworkScore class would provide a nice place to centralize score constants
// so they are not scattered about the transports.
- int score = currentScore;
+ // If this network is explicitly selected and the user has decided to use it even if it's
+ // unvalidated, give it the maximum score. Also give it the maximum score if it's explicitly
+ // selected and we're trying to see what its score could be. This ensures that we don't tear
+ // down an explicitly selected network before the user gets a chance to prefer it when
+ // a higher-scoring network (e.g., Ethernet) is available.
+ if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) {
+ return MAXIMUM_NETWORK_SCORE;
+ }
+ int score = currentScore;
if (!everValidated && !pretendValidated) score -= UNVALIDATED_SCORE_PENALTY;
if (score < 0) score = 0;
-
- if (networkMisc.explicitlySelected) score = EXPLICITLY_SELECTED_NETWORK_SCORE;
-
return score;
}
@@ -153,7 +164,9 @@ public class NetworkAgentInfo {
networkCapabilities + "} Score{" + getCurrentScore() + "} " +
"everValidated{" + everValidated + "} lastValidated{" + lastValidated + "} " +
"created{" + created + "} " +
- "explicitlySelected{" + networkMisc.explicitlySelected + "} }";
+ "explicitlySelected{" + networkMisc.explicitlySelected + "} " +
+ "acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} " +
+ "}";
}
public String name() {
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index d0e1665..7e20276 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -216,7 +216,7 @@ public class NetworkMonitor extends StateMachine {
// If a network is not validated, make one attempt every 10 mins to see if it starts working.
private static final int REEVALUATE_PAUSE_MS = 10*60*1000;
private static final int PERIODIC_ATTEMPTS = 1;
- // When an application calls reportBadNetwork, only make one attempt.
+ // When an application calls reportNetworkConnectivity, only make one attempt.
private static final int REEVALUATE_ATTEMPTS = 1;
private final int mReevaluateDelayMs;
private int mReevaluateToken = 0;
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 0b430ea..3d478f9 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -17,8 +17,13 @@
package com.android.server.connectivity;
import static android.Manifest.permission.BIND_VPN_SERVICE;
+import static android.net.ConnectivityManager.NETID_UNSET;
import static android.net.RouteInfo.RTN_THROW;
import static android.net.RouteInfo.RTN_UNREACHABLE;
+import static android.os.UserHandle.PER_USER_RANGE;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+
import android.Manifest;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -29,6 +34,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
@@ -64,6 +70,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.security.Credentials;
import android.security.KeyStore;
+import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -110,7 +117,6 @@ public class Vpn {
private LegacyVpnRunner mLegacyVpnRunner;
private PendingIntent mStatusIntent;
private volatile boolean mEnableTeardown = true;
- private final IConnectivityManager mConnService;
private final INetworkManagementService mNetd;
private VpnConfig mConfig;
private NetworkAgent mNetworkAgent;
@@ -126,10 +132,9 @@ public class Vpn {
private final int mUserHandle;
public Vpn(Looper looper, Context context, INetworkManagementService netService,
- IConnectivityManager connService, int userHandle) {
+ int userHandle) {
mContext = context;
mNetd = netService;
- mConnService = connService;
mUserHandle = userHandle;
mLooper = looper;
@@ -336,6 +341,10 @@ public class Vpn {
return mNetworkInfo;
}
+ public int getNetId() {
+ return mNetworkAgent != null ? mNetworkAgent.netId : NETID_UNSET;
+ }
+
private LinkProperties makeLinkProperties() {
boolean allowIPv4 = mConfig.allowIPv4;
boolean allowIPv6 = mConfig.allowIPv6;
@@ -1106,11 +1115,15 @@ public class Vpn {
// registering
mOuterInterface = mConfig.interfaze;
- try {
- mOuterConnection.set(
- mConnService.findConnectionTypeForIface(mOuterInterface));
- } catch (Exception e) {
- mOuterConnection.set(ConnectivityManager.TYPE_NONE);
+ if (!TextUtils.isEmpty(mOuterInterface)) {
+ final ConnectivityManager cm = ConnectivityManager.from(mContext);
+ for (Network network : cm.getAllNetworks()) {
+ final LinkProperties lp = cm.getLinkProperties(network);
+ if (lp != null && mOuterInterface.equals(lp.getInterfaceName())) {
+ final NetworkInfo networkInfo = cm.getNetworkInfo(network);
+ if (networkInfo != null) mOuterConnection.set(networkInfo.getType());
+ }
+ }
}
IntentFilter filter = new IntentFilter();
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index e16be71..ee36972 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -47,6 +47,10 @@ abstract class DisplayDevice {
// within a transaction from performTraversalInTransactionLocked.
private Surface mCurrentSurface;
+ // DEBUG STATE: Last device info which was written to the log, or null if none.
+ // Do not use for any other purpose.
+ DisplayDeviceInfo mDebugLastLoggedDeviceInfo;
+
public DisplayDevice(DisplayAdapter displayAdapter, IBinder displayToken, String uniqueId) {
mDisplayAdapter = displayAdapter;
mDisplayToken = displayToken;
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index d1e73f0..ebf6e4e 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -104,6 +104,16 @@ final class DisplayDeviceInfo {
public static final int TOUCH_EXTERNAL = 2;
/**
+ * Diff result: The {@link #state} fields differ.
+ */
+ public static final int DIFF_STATE = 1 << 0;
+
+ /**
+ * Diff result: Other fields differ.
+ */
+ public static final int DIFF_OTHER = 1 << 1;
+
+ /**
* Gets the name of the display device, which may be derived from EDID or
* other sources. The name may be localized and displayed to the user.
*/
@@ -238,26 +248,39 @@ final class DisplayDeviceInfo {
}
public boolean equals(DisplayDeviceInfo other) {
- return other != null
- && Objects.equal(name, other.name)
- && Objects.equal(uniqueId, other.uniqueId)
- && width == other.width
- && height == other.height
- && refreshRate == other.refreshRate
- && Arrays.equals(supportedRefreshRates, other.supportedRefreshRates)
- && densityDpi == other.densityDpi
- && xDpi == other.xDpi
- && yDpi == other.yDpi
- && appVsyncOffsetNanos == other.appVsyncOffsetNanos
- && presentationDeadlineNanos == other.presentationDeadlineNanos
- && flags == other.flags
- && touch == other.touch
- && rotation == other.rotation
- && type == other.type
- && Objects.equal(address, other.address)
- && state == other.state
- && ownerUid == other.ownerUid
- && Objects.equal(ownerPackageName, other.ownerPackageName);
+ return other != null && diff(other) == 0;
+ }
+
+ /**
+ * Computes the difference between display device infos.
+ * Assumes other is not null.
+ */
+ public int diff(DisplayDeviceInfo other) {
+ int diff = 0;
+ if (state != other.state) {
+ diff |= DIFF_STATE;
+ }
+ if (!Objects.equal(name, other.name)
+ || !Objects.equal(uniqueId, other.uniqueId)
+ || width != other.width
+ || height != other.height
+ || refreshRate != other.refreshRate
+ || !Arrays.equals(supportedRefreshRates, other.supportedRefreshRates)
+ || densityDpi != other.densityDpi
+ || xDpi != other.xDpi
+ || yDpi != other.yDpi
+ || appVsyncOffsetNanos != other.appVsyncOffsetNanos
+ || presentationDeadlineNanos != other.presentationDeadlineNanos
+ || flags != other.flags
+ || touch != other.touch
+ || rotation != other.rotation
+ || type != other.type
+ || !Objects.equal(address, other.address)
+ || ownerUid != other.ownerUid
+ || !Objects.equal(ownerPackageName, other.ownerPackageName)) {
+ diff |= DIFF_OTHER;
+ }
+ return diff;
}
@Override
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 9ea0444..1e87433 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -668,13 +668,14 @@ public final class DisplayManagerService extends SystemService {
}
private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
+ DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
if (mDisplayDevices.contains(device)) {
- Slog.w(TAG, "Attempted to add already added display device: "
- + device.getDisplayDeviceInfoLocked());
+ Slog.w(TAG, "Attempted to add already added display device: " + info);
return;
}
- Slog.i(TAG, "Display device added: " + device.getDisplayDeviceInfoLocked());
+ Slog.i(TAG, "Display device added: " + info);
+ device.mDebugLastLoggedDeviceInfo = info;
mDisplayDevices.add(device);
addLogicalDisplayLocked(device);
@@ -687,13 +688,20 @@ public final class DisplayManagerService extends SystemService {
private void handleDisplayDeviceChanged(DisplayDevice device) {
synchronized (mSyncRoot) {
+ DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
if (!mDisplayDevices.contains(device)) {
- Slog.w(TAG, "Attempted to change non-existent display device: "
- + device.getDisplayDeviceInfoLocked());
+ Slog.w(TAG, "Attempted to change non-existent display device: " + info);
return;
}
- Slog.i(TAG, "Display device changed: " + device.getDisplayDeviceInfoLocked());
+ int diff = device.mDebugLastLoggedDeviceInfo.diff(info);
+ if (diff == DisplayDeviceInfo.DIFF_STATE) {
+ Slog.i(TAG, "Display device changed state: \"" + info.name
+ + "\", " + Display.stateToString(info.state));
+ } else if (diff != 0) {
+ Slog.i(TAG, "Display device changed: " + info);
+ }
+ device.mDebugLastLoggedDeviceInfo = info;
device.applyPendingDisplayDeviceInfoChangesLocked();
if (updateLogicalDisplaysLocked()) {
@@ -708,13 +716,14 @@ public final class DisplayManagerService extends SystemService {
}
}
private void handleDisplayDeviceRemovedLocked(DisplayDevice device) {
+ DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
if (!mDisplayDevices.remove(device)) {
- Slog.w(TAG, "Attempted to remove non-existent display device: "
- + device.getDisplayDeviceInfoLocked());
+ Slog.w(TAG, "Attempted to remove non-existent display device: " + info);
return;
}
- Slog.i(TAG, "Display device removed: " + device.getDisplayDeviceInfoLocked());
+ Slog.i(TAG, "Display device removed: " + info);
+ device.mDebugLastLoggedDeviceInfo = info;
updateLogicalDisplaysLocked();
scheduleTraversalLocked(false);
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 3bb7818..65dc72f 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -302,7 +302,10 @@ final class LogicalDisplay {
// multiplying the fractions by the product of their denominators before
// comparing them.
int displayRectWidth, displayRectHeight;
- if (physWidth * displayInfo.logicalHeight
+ if ((displayInfo.flags & Display.FLAG_SCALING_DISABLED) != 0) {
+ displayRectWidth = displayInfo.logicalWidth;
+ displayRectHeight = displayInfo.logicalHeight;
+ } else if (physWidth * displayInfo.logicalHeight
< physHeight * displayInfo.logicalWidth) {
// Letter box.
displayRectWidth = physWidth;
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index c150b60..0ca0c0e 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -39,6 +39,7 @@ import static android.Manifest.permission.USE_FINGERPRINT;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -51,9 +52,9 @@ import java.util.List;
public class FingerprintService extends SystemService {
private static final String TAG = "FingerprintService";
private static final boolean DEBUG = true;
- private ClientData mAuthClient = null;
- private ClientData mEnrollClient = null;
- private ClientData mRemoveClient = null;
+ private ClientMonitor mAuthClient = null;
+ private ClientMonitor mEnrollClient = null;
+ private ClientMonitor mRemoveClient = null;
private static final int MSG_NOTIFY = 10;
@@ -63,7 +64,6 @@ public class FingerprintService extends SystemService {
// Must agree with the list in fingerprint.h
private static final int FINGERPRINT_ERROR = -1;
private static final int FINGERPRINT_ACQUIRED = 1;
- private static final int FINGERPRINT_PROCESSED = 2;
private static final int FINGERPRINT_TEMPLATE_ENROLLING = 3;
private static final int FINGERPRINT_TEMPLATE_REMOVED = 4;
private static final int FINGERPRINT_AUTHENTICATED = 5;
@@ -83,80 +83,21 @@ public class FingerprintService extends SystemService {
};
private Context mContext;
private int mHalDeviceId;
+ private int mFailedAttempts;
+ private final Runnable mLockoutReset = new Runnable() {
+ @Override
+ public void run() {
+ resetFailedAttempts();
+ }
+ };
private static final int STATE_IDLE = 0;
private static final int STATE_AUTHENTICATING = 1;
private static final int STATE_ENROLLING = 2;
private static final int STATE_REMOVING = 3;
private static final long MS_PER_SEC = 1000;
-
- private class ClientData {
- IBinder token;
- IFingerprintServiceReceiver receiver;
- int userId;
- long opId;
- private TokenWatcher tokenWatcher;
- public ClientData(IBinder token, long opId, IFingerprintServiceReceiver receiver,
- int userId) {
- this.token = token;
- this.opId = opId;
- this.receiver = receiver;
- this.userId = userId;
- tokenWatcher = new TokenWatcher(token);
- try {
- token.linkToDeath(tokenWatcher, 0);
- } catch (RemoteException e) {
- Slog.w(TAG, "caught remote exception in linkToDeath: ", e);
- }
- }
-
- IBinder getToken() {
- return tokenWatcher.getToken();
- }
-
- public void destroy() {
- token.unlinkToDeath(tokenWatcher, 0);
- tokenWatcher.token = null;
- }
- }
-
- private class TokenWatcher implements IBinder.DeathRecipient {
- WeakReference<IBinder> token;
-
- TokenWatcher(IBinder token) {
- this.token = new WeakReference<IBinder>(token);
- }
-
- IBinder getToken() {
- return token.get();
- }
-
- public void binderDied() {
- if (mAuthClient != null & mAuthClient.token == token)
- mAuthClient = null;
- if (mEnrollClient != null && mEnrollClient.token == token)
- mEnrollClient = null;
- this.token = null;
- }
-
- protected void finalize() throws Throwable {
- try {
- if (token != null) {
- if (DEBUG) Slog.w(TAG, "removing leaked reference: " + token);
- if (mAuthClient != null && mAuthClient.token == token) {
- mAuthClient.destroy();
- mAuthClient = null;
- }
- if (mEnrollClient != null && mEnrollClient.token == token) {
- mAuthClient.destroy();
- mEnrollClient = null;
- }
- }
- } finally {
- super.finalize();
- }
- }
- }
+ private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30*1000;
+ private static final int MAX_FAILED_ATTEMPTS = 5;
public FingerprintService(Context context) {
super(context);
@@ -166,11 +107,11 @@ public class FingerprintService extends SystemService {
// TODO: Move these into separate process
// JNI methods to communicate from FingerprintService to HAL
- static native int nativeEnroll(long challenge, int groupId, int timeout);
+ static native int nativeEnroll(byte [] token, int groupId, int timeout);
static native long nativePreEnroll();
static native int nativeStopEnrollment();
static native int nativeAuthenticate(long sessionId, int groupId);
- static native int nativeStopAuthentication(long sessionId);
+ static native int nativeStopAuthentication();
static native int nativeRemove(int fingerId, int groupId);
static native int nativeOpenHal();
static native int nativeCloseHal();
@@ -201,82 +142,62 @@ public class FingerprintService extends SystemService {
Slog.v(TAG, "handleNotify(type=" + type + ", arg1=" + arg1 + ", arg2=" + arg2 + ")"
+ ", mAuthClients = " + mAuthClient + ", mEnrollClient = " + mEnrollClient);
if (mEnrollClient != null) {
- try {
- final IBinder token = mEnrollClient.token;
- if (doNotify(mEnrollClient, type, arg1, arg2, arg3)) {
- stopEnrollment(token);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "can't send message to mEnrollClient. Did it die?", e);
- mEnrollClient.destroy();
- mEnrollClient = null;
+ final IBinder token = mEnrollClient.token;
+ if (doNotify(mEnrollClient, type, arg1, arg2, arg3)) {
+ stopEnrollment(token);
}
}
if (mAuthClient != null) {
- try {
- final IBinder token = mAuthClient.getToken();
- if (doNotify(mAuthClient, type, arg1, arg2, arg3)) {
- stopAuthentication(token);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "can't send message to mAuthClient. Did it die?", e);
- mAuthClient.destroy();
- mAuthClient = null;
+ final IBinder token = mAuthClient.token;
+ if (doNotify(mAuthClient, type, arg1, arg2, arg3)) {
+ stopAuthentication(token);
}
}
if (mRemoveClient != null) {
- try {
- if (doNotify(mRemoveClient, type, arg1, arg2, arg3)) {
- mRemoveClient.destroy();
- mRemoveClient = null;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "can't send message to mRemoveClient. Did it die?", e);
- mRemoveClient.destroy();
- mRemoveClient = null;
+ if (doNotify(mRemoveClient, type, arg1, arg2, arg3)) {
+ removeClient(mRemoveClient);
}
}
}
// Returns true if the operation is done, i.e. authentication completed
- boolean doNotify(ClientData clientData, int type, int arg1, int arg2, int arg3)
- throws RemoteException {
- if (clientData.receiver == null) {
- if (DEBUG) Slog.v(TAG, "receiver not registered!!");
- return false;
- }
+ boolean doNotify(ClientMonitor clientMonitor, int type, int arg1, int arg2, int arg3) {
ContentResolver contentResolver = mContext.getContentResolver();
boolean operationCompleted = false;
switch (type) {
case FINGERPRINT_ERROR:
- clientData.receiver.onError(mHalDeviceId, arg1 /* error */);
- if (arg1 == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
- if (mEnrollClient != null) {
- mEnrollClient.destroy();
- mEnrollClient = null;
- }
- if (mAuthClient != null) {
- mAuthClient.destroy();
- mAuthClient = null;
- }
+ {
+ final int error = arg1;
+ clientMonitor.sendError(error);
+ removeClient(clientMonitor);
+ operationCompleted = true; // any error means the operation is done
}
- operationCompleted = true; // any error means the operation is done
break;
case FINGERPRINT_ACQUIRED:
- clientData.receiver.onAcquired(mHalDeviceId, arg1 /* acquireInfo */);
+ clientMonitor.sendAcquired(arg1 /* acquireInfo */);
break;
- case FINGERPRINT_PROCESSED:
- clientData.receiver.onProcessed(mHalDeviceId, arg1 /* fpId */, arg2 /* gpId */);
- operationCompleted = true; // we either got a positive or negative match
+ case FINGERPRINT_AUTHENTICATED:
+ {
+ final int fpId = arg1;
+ final int groupId = arg2;
+ clientMonitor.sendAuthenticated(fpId, groupId);
+ if (fpId == 0) {
+ if (clientMonitor == mAuthClient) {
+ operationCompleted = handleFailedAttempt(clientMonitor);
+ }
+ } else {
+ mLockoutReset.run(); // a valid fingerprint resets lockout
+ }
+ }
break;
case FINGERPRINT_TEMPLATE_ENROLLING:
{
final int fpId = arg1;
final int groupId = arg2;
final int remaining = arg3;
- clientData.receiver.onEnrollResult(mHalDeviceId, fpId, groupId, remaining);
+ clientMonitor.sendEnrollResult(fpId, groupId, remaining);
if (remaining == 0) {
- addTemplateForUser(clientData, contentResolver, fpId);
+ addTemplateForUser(clientMonitor, contentResolver, fpId);
operationCompleted = true; // enroll completed
}
}
@@ -285,11 +206,11 @@ public class FingerprintService extends SystemService {
{
final int fingerId = arg1;
final int groupId = arg2;
- removeTemplateForUser(clientData, contentResolver, fingerId);
+ removeTemplateForUser(clientMonitor, contentResolver, fingerId);
if (fingerId == 0) {
operationCompleted = true; // remove completed
} else {
- clientData.receiver.onRemoved(mHalDeviceId, fingerId, groupId);
+ clientMonitor.sendRemoved(fingerId, groupId);
}
}
break;
@@ -297,24 +218,60 @@ public class FingerprintService extends SystemService {
return operationCompleted;
}
- private void removeTemplateForUser(ClientData clientData, ContentResolver contentResolver,
+ private void removeClient(ClientMonitor clientMonitor) {
+ if (clientMonitor == null) return;
+ clientMonitor.destroy();
+ if (clientMonitor == mAuthClient) {
+ mAuthClient = null;
+ } else if (clientMonitor == mEnrollClient) {
+ mEnrollClient = null;
+ } else if (clientMonitor == mRemoveClient) {
+ mRemoveClient = null;
+ }
+ }
+
+ private boolean inLockoutMode() {
+ return mFailedAttempts > MAX_FAILED_ATTEMPTS;
+ }
+
+ private void resetFailedAttempts() {
+ if (DEBUG) Slog.v(TAG, "Reset fingerprint lockout");
+ mFailedAttempts = 0;
+ }
+
+ private boolean handleFailedAttempt(ClientMonitor clientMonitor) {
+ mFailedAttempts++;
+ if (mFailedAttempts > MAX_FAILED_ATTEMPTS) {
+ // Failing multiple times will continue to push out the lockout time.
+ mHandler.removeCallbacks(mLockoutReset);
+ mHandler.postDelayed(mLockoutReset, FAIL_LOCKOUT_TIMEOUT_MS);
+ if (clientMonitor != null
+ && !clientMonitor.sendError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)) {
+ Slog.w(TAG, "Cannot send lockout message to client");
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void removeTemplateForUser(ClientMonitor clientMonitor, ContentResolver contentResolver,
final int fingerId) {
FingerprintUtils.removeFingerprintIdForUser(fingerId, contentResolver,
- clientData.userId);
+ clientMonitor.userId);
}
- private void addTemplateForUser(ClientData clientData, ContentResolver contentResolver,
+ private void addTemplateForUser(ClientMonitor clientMonitor, ContentResolver contentResolver,
final int fingerId) {
FingerprintUtils.addFingerprintIdForUser(contentResolver, fingerId,
- clientData.userId);
+ clientMonitor.userId);
}
- void startEnrollment(IBinder token, long opId,
- int groupId, IFingerprintServiceReceiver receiver, int flags) {
+ void startEnrollment(IBinder token, byte[] cryptoToken, int groupId,
+ IFingerprintServiceReceiver receiver, int flags) {
stopPendingOperations();
- mEnrollClient = new ClientData(token, opId, receiver, groupId);
+ mEnrollClient = new ClientMonitor(token, receiver, groupId);
final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
- final int result = nativeEnroll(opId, groupId, timeout);
+ final int result = nativeEnroll(cryptoToken, groupId, timeout);
if (result != 0) {
Slog.w(TAG, "startEnroll failed, result=" + result);
}
@@ -335,8 +292,11 @@ public class FingerprintService extends SystemService {
}
void stopEnrollment(IBinder token) {
- if (mEnrollClient == null || mEnrollClient.token != token) return;
+ final ClientMonitor client = mEnrollClient;
+ if (client == null || client.token != token) return;
int result = nativeStopEnrollment();
+ client.sendError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
+ removeClient(mEnrollClient);
if (result != 0) {
Slog.w(TAG, "startEnrollCancel failed, result=" + result);
}
@@ -345,7 +305,15 @@ public class FingerprintService extends SystemService {
void startAuthentication(IBinder token, long opId, int groupId,
IFingerprintServiceReceiver receiver, int flags) {
stopPendingOperations();
- mAuthClient = new ClientData(token, opId, receiver, groupId);
+ mAuthClient = new ClientMonitor(token, receiver, groupId);
+ if (inLockoutMode()) {
+ Slog.v(TAG, "In lockout mode; disallowing authentication");
+ if (!mAuthClient.sendError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)) {
+ Slog.w(TAG, "Cannot send timeout message to client");
+ }
+ mAuthClient = null;
+ return;
+ }
final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
final int result = nativeAuthenticate(opId, groupId);
if (result != 0) {
@@ -354,8 +322,11 @@ public class FingerprintService extends SystemService {
}
void stopAuthentication(IBinder token) {
- if (mAuthClient == null || mAuthClient.token != token) return;
- int result = nativeStopAuthentication(mAuthClient.opId);
+ final ClientMonitor client = mAuthClient;
+ if (client == null || client.token != token) return;
+ int result = nativeStopAuthentication();
+ client.sendError(FingerprintManager.FINGERPRINT_ERROR_CANCELED);
+ removeClient(mAuthClient);
if (result != 0) {
Slog.w(TAG, "stopAuthentication failed, result=" + result);
}
@@ -363,7 +334,7 @@ public class FingerprintService extends SystemService {
void startRemove(IBinder token, int fingerId, int userId,
IFingerprintServiceReceiver receiver) {
- mRemoveClient = new ClientData(token, 0, receiver, userId);
+ mRemoveClient = new ClientMonitor(token, receiver, userId);
// The fingerprint template ids will be removed when we get confirmation from the HAL
final int result = nativeRemove(fingerId, userId);
if (result != 0) {
@@ -387,22 +358,124 @@ public class FingerprintService extends SystemService {
return result;
}
+ public boolean hasEnrolledFingerprints(int groupId) {
+ ContentResolver resolver = mContext.getContentResolver();
+ return FingerprintUtils.getFingerprintIdsForUser(resolver, groupId).length > 0;
+ }
+
void checkPermission(String permission) {
getContext().enforceCallingOrSelfPermission(permission,
"Must have " + permission + " permission.");
}
- private static final class Message {
+ private class ClientMonitor implements IBinder.DeathRecipient {
IBinder token;
- long opId;
- int groupId;
- int flags;
+ WeakReference<IFingerprintServiceReceiver> receiver;
+ int userId;
- public Message(IBinder token, long challenge, int groupId, int flags) {
+ public ClientMonitor(IBinder token, IFingerprintServiceReceiver receiver, int userId) {
this.token = token;
- this.opId = challenge;
- this.groupId = groupId;
- this.flags = flags;
+ this.receiver = new WeakReference<IFingerprintServiceReceiver>(receiver);
+ this.userId = userId;
+ try {
+ token.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "caught remote exception in linkToDeath: ", e);
+ }
+ }
+
+ public void destroy() {
+ if (token != null) {
+ token.unlinkToDeath(this, 0);
+ token = null;
+ }
+ receiver = null;
+ }
+
+ public void binderDied() {
+ token = null;
+ removeClient(this);
+ }
+
+ protected void finalize() throws Throwable {
+ try {
+ if (token != null) {
+ if (DEBUG) Slog.w(TAG, "removing leaked reference: " + token);
+ removeClient(this);
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private boolean sendRemoved(int fingerId, int groupId) {
+ IFingerprintServiceReceiver rx = receiver.get();
+ if (rx != null) {
+ try {
+ rx.onRemoved(mHalDeviceId, fingerId, groupId);
+ return true;
+ } catch (RemoteException e) {
+ if (DEBUG) Slog.v(TAG, "Failed to invoke sendRemoved:", e);
+ }
+ }
+ removeClient(this);
+ return false;
+ }
+
+ private boolean sendEnrollResult(int fpId, int groupId, int remaining) {
+ IFingerprintServiceReceiver rx = receiver.get();
+ if (rx != null) {
+ try {
+ rx.onEnrollResult(mHalDeviceId, fpId, groupId, remaining);
+ return true;
+ } catch (RemoteException e) {
+ if (DEBUG) Slog.v(TAG, "Failed to invoke sendEnrollResult:", e);
+ }
+ }
+ removeClient(this);
+ return false;
+ }
+
+ private boolean sendAuthenticated(int fpId, int groupId) {
+ IFingerprintServiceReceiver rx = receiver.get();
+ if (rx != null) {
+ try {
+ rx.onAuthenticated(mHalDeviceId, fpId, groupId);
+ return true;
+ } catch (RemoteException e) {
+ if (DEBUG) Slog.v(TAG, "Failed to invoke sendProcessed:", e);
+ }
+ }
+ removeClient(this);
+ return false;
+ }
+
+ private boolean sendAcquired(int acquiredInfo) {
+ IFingerprintServiceReceiver rx = receiver.get();
+ if (rx != null) {
+ try {
+ rx.onAcquired(mHalDeviceId, acquiredInfo);
+ return true;
+ } catch (RemoteException e) {
+ if (DEBUG) Slog.v(TAG, "Failed to invoke sendAcquired:", e);
+ }
+ }
+ removeClient(this);
+ return false;
+ }
+
+ private boolean sendError(int error) {
+ IFingerprintServiceReceiver rx = receiver.get();
+ if (rx != null) {
+ try {
+ rx.onError(mHalDeviceId, error);
+ return true;
+ } catch (RemoteException e) {
+ if (DEBUG) Slog.v(TAG, "Failed to invoke sendError:", e);
+ }
+ }
+ removeClient(this);
+ return false;
}
}
@@ -415,13 +488,14 @@ public class FingerprintService extends SystemService {
@Override
// Binder call
- public void enroll(final IBinder token, final long opid, final int groupId,
+ public void enroll(final IBinder token, final byte[] cryptoToken, final int groupId,
final IFingerprintServiceReceiver receiver, final int flags) {
checkPermission(MANAGE_FINGERPRINT);
+ final byte [] cryptoClone = Arrays.copyOf(cryptoToken, cryptoToken.length);
mHandler.post(new Runnable() {
@Override
public void run() {
- startEnrollment(token, opid, groupId, receiver, flags);
+ startEnrollment(token, cryptoClone, groupId, receiver, flags);
}
});
}
@@ -503,6 +577,13 @@ public class FingerprintService extends SystemService {
checkPermission(USE_FINGERPRINT);
return FingerprintService.this.getEnrolledFingerprints(groupId);
}
+
+ @Override
+ // Binder call
+ public boolean hasEnrolledFingerprints(int groupId) {
+ checkPermission(USE_FINGERPRINT);
+ return FingerprintService.this.hasEnrolledFingerprints(groupId);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 4ac2b48..94f8dee 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -913,6 +913,12 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
}
}
+ @ServiceThreadOnly
+ boolean isConnected(int portId) {
+ assertRunOnServiceThread();
+ return mService.isConnected(portId);
+ }
+
private void notifyArcStatusToAudioService(boolean enabled) {
// Note that we don't set any name to ARC.
mService.getAudioManager().setWiredDeviceConnectionState(
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 49a96d8..2cbc1b9 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -780,6 +780,12 @@ public final class HdmiControlService extends SystemService {
return false;
}
+ @ServiceThreadOnly
+ boolean isConnected(int portId) {
+ assertRunOnServiceThread();
+ return mCecController.isConnected(portId);
+ }
+
void runOnServiceThread(Runnable runnable) {
mHandler.post(runnable);
}
diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
index a944a27..5f2d651 100644
--- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
+++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
@@ -156,10 +156,13 @@ final class HotplugDetectionAction extends HdmiCecFeatureAction {
int index = -1;
while ((index = removed.nextSetBit(index + 1)) != -1) {
if (index == Constants.ADDR_AUDIO_SYSTEM) {
- ++mAvrStatusCount;
- Slog.w(TAG, "Ack not returned from AVR. count: " + mAvrStatusCount);
- if (mAvrStatusCount < AVR_COUNT_MAX) {
- continue;
+ HdmiDeviceInfo avr = tv().getAvrDeviceInfo();
+ if (avr != null && tv().isConnected(avr.getPortId())) {
+ ++mAvrStatusCount;
+ Slog.w(TAG, "Ack not returned from AVR. count: " + mAvrStatusCount);
+ if (mAvrStatusCount < AVR_COUNT_MAX) {
+ continue;
+ }
}
}
Slog.v(TAG, "Remove device by hot-plug detection:" + index);
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
index 23d5c05..98fb11b 100644
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -107,7 +107,16 @@ public class AppIdleController extends StateController
@Override
public void dumpControllerState(PrintWriter pw) {
- // TODO:
+ pw.println("AppIdle");
+ pw.println("Plugged In: " + mPluggedIn);
+ synchronized (mTrackedTasks) {
+ for (JobStatus task : mTrackedTasks) {
+ pw.print(task.job.getService().getPackageName());
+ pw.print(":idle=" + !task.appNotIdleConstraintSatisfied.get());
+ pw.print(", ");
+ }
+ pw.println();
+ }
}
@Override
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index 7c41abb..a279e0f 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -197,6 +197,8 @@ public class GpsLocationProvider implements LocationProviderInterface {
private static final int REMOVE_LISTENER = 9;
private static final int INJECT_NTP_TIME_FINISHED = 10;
private static final int DOWNLOAD_XTRA_DATA_FINISHED = 11;
+ private static final int SUBSCRIPTION_OR_SIM_CHANGED = 12;
+ private static final int INITIALIZE_HANDLER = 13;
// Request setid
private static final int AGPS_RIL_REQUEST_SETID_IMSI = 1;
@@ -338,8 +340,12 @@ public class GpsLocationProvider implements LocationProviderInterface {
// True if gps should be disabled (used to support battery saver mode in settings).
private boolean mDisableGps = false;
- // properties loaded from PROPERTIES_FILE
+ /**
+ * Properties loaded from PROPERTIES_FILE.
+ * It must be accessed only inside {@link #mHandler}.
+ */
private Properties mProperties;
+
private String mSuplServerHost;
private int mSuplServerPort = TCP_MIN_PORT;
private String mC2KServerHost;
@@ -462,7 +468,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
new OnSubscriptionsChangedListener() {
@Override
public void onSubscriptionsChanged() {
- subscriptionOrSimChanged(mContext);
+ sendMessage(SUBSCRIPTION_OR_SIM_CHANGED, 0, null);
}
};
@@ -627,53 +633,22 @@ public class GpsLocationProvider implements LocationProviderInterface {
mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
BatteryStats.SERVICE_NAME));
- // Load GPS configuration.
+ // Construct internal handler
+ mHandler = new ProviderHandler(looper);
+
+ // Load GPS configuration and register listeners in the background:
+ // some operations, such as opening files and registering broadcast receivers, can take a
+ // relative long time, so the ctor() is kept to create objects needed by this instance,
+ // while IO initialization and registration is delegated to our internal handler
+ // this approach is just fine because events are posted to our handler anyway
mProperties = new Properties();
- reloadGpsProperties(mContext, mProperties);
+ sendMessage(INITIALIZE_HANDLER, 0, null);
// Create a GPS net-initiated handler.
mNIHandler = new GpsNetInitiatedHandler(context,
mNetInitiatedListener,
mSuplEsEnabled);
- // TODO: When this object "finishes" we should unregister by invoking
- // SubscriptionManager.getInstance(mContext).unregister(mOnSubscriptionsChangedListener);
- // This is not strictly necessary because it will be unregistered if the
- // notification fails but it is good form.
-
- // Register for SubscriptionInfo list changes which is guaranteed
- // to invoke onSubscriptionsChanged the first time.
- SubscriptionManager.from(mContext)
- .addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
-
- // construct handler, listen for events
- mHandler = new ProviderHandler(looper);
- listenForBroadcasts();
-
- // also listen for PASSIVE_PROVIDER updates
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- LocationManager locManager =
- (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
- final long minTime = 0;
- final float minDistance = 0;
- final boolean oneShot = false;
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(
- LocationManager.PASSIVE_PROVIDER,
- minTime,
- minDistance,
- oneShot);
- // Don't keep track of this request since it's done on behalf of other clients
- // (which are kept track of separately).
- request.setHideFromAppOps(true);
- locManager.requestLocationUpdates(
- request,
- new NetworkLocationListener(),
- mHandler.getLooper());
- }
- });
-
mListenerHelper = new GpsStatusListenerHelper(mHandler) {
@Override
protected boolean isAvailableInPlatform() {
@@ -731,33 +706,6 @@ public class GpsLocationProvider implements LocationProviderInterface {
};
}
- private void listenForBroadcasts() {
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intents.DATA_SMS_RECEIVED_ACTION);
- intentFilter.addDataScheme("sms");
- intentFilter.addDataAuthority("localhost","7275");
- mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, mHandler);
-
- intentFilter = new IntentFilter();
- intentFilter.addAction(Intents.WAP_PUSH_RECEIVED_ACTION);
- try {
- intentFilter.addDataType("application/vnd.omaloc-supl-init");
- } catch (IntentFilter.MalformedMimeTypeException e) {
- Log.w(TAG, "Malformed SUPL init mime type");
- }
- mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, mHandler);
-
- intentFilter = new IntentFilter();
- intentFilter.addAction(ALARM_WAKEUP);
- intentFilter.addAction(ALARM_TIMEOUT);
- intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE);
- intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
- intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
- intentFilter.addAction(Intent.ACTION_SCREEN_ON);
- intentFilter.addAction(SIM_STATE_CHANGED);
- mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, mHandler);
- }
-
/**
* Returns the name of this provider.
*/
@@ -2012,13 +1960,85 @@ public class GpsLocationProvider implements LocationProviderInterface {
case UPDATE_LOCATION:
handleUpdateLocation((Location)msg.obj);
break;
+ case SUBSCRIPTION_OR_SIM_CHANGED:
+ subscriptionOrSimChanged(mContext);
+ break;
+ case INITIALIZE_HANDLER:
+ initialize();
+ break;
}
if (msg.arg2 == 1) {
// wakelock was taken for this message, release it
mWakeLock.release();
}
}
- };
+
+ /**
+ * This method is bound to {@link #GpsLocationProvider(Context, ILocationManager, Looper)}.
+ * It is in charge of loading properties and registering for events that will be posted to
+ * this handler.
+ */
+ private void initialize() {
+ // load default GPS configuration
+ // (this configuration might change in the future based on SIM changes)
+ reloadGpsProperties(mContext, mProperties);
+
+ // TODO: When this object "finishes" we should unregister by invoking
+ // SubscriptionManager.getInstance(mContext).unregister(mOnSubscriptionsChangedListener);
+ // This is not strictly necessary because it will be unregistered if the
+ // notification fails but it is good form.
+
+ // Register for SubscriptionInfo list changes which is guaranteed
+ // to invoke onSubscriptionsChanged the first time.
+ SubscriptionManager.from(mContext)
+ .addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
+
+ // listen for events
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intents.DATA_SMS_RECEIVED_ACTION);
+ intentFilter.addDataScheme("sms");
+ intentFilter.addDataAuthority("localhost","7275");
+ mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
+
+ intentFilter = new IntentFilter();
+ intentFilter.addAction(Intents.WAP_PUSH_RECEIVED_ACTION);
+ try {
+ intentFilter.addDataType("application/vnd.omaloc-supl-init");
+ } catch (IntentFilter.MalformedMimeTypeException e) {
+ Log.w(TAG, "Malformed SUPL init mime type");
+ }
+ mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
+
+ intentFilter = new IntentFilter();
+ intentFilter.addAction(ALARM_WAKEUP);
+ intentFilter.addAction(ALARM_TIMEOUT);
+ intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE);
+ intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
+ intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ intentFilter.addAction(Intent.ACTION_SCREEN_ON);
+ intentFilter.addAction(SIM_STATE_CHANGED);
+ mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
+
+ // listen for PASSIVE_PROVIDER updates
+ LocationManager locManager =
+ (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
+ long minTime = 0;
+ float minDistance = 0;
+ boolean oneShot = false;
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ LocationManager.PASSIVE_PROVIDER,
+ minTime,
+ minDistance,
+ oneShot);
+ // Don't keep track of this request since it's done on behalf of other clients
+ // (which are kept track of separately).
+ request.setHideFromAppOps(true);
+ locManager.requestLocationUpdates(
+ request,
+ new NetworkLocationListener(),
+ getLooper());
+ }
+ }
private final class NetworkLocationListener implements LocationListener {
@Override
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4cf2909..997d546 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -29,9 +29,11 @@ import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.INotificationManager;
+import android.app.INotificationManagerCallback;
import android.app.ITransientNotification;
import android.app.Notification;
import android.app.NotificationManager;
+import android.app.NotificationManager.Policy;
import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
@@ -227,6 +229,8 @@ public class NotificationManagerService extends SystemService {
new ArrayMap<String, NotificationRecord>();
final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
+ private final ArrayMap<String, Policy.Token> mPolicyTokens = new ArrayMap<>();
+
// The last key in this list owns the hardware.
ArrayList<String> mLights = new ArrayList<>();
@@ -893,6 +897,13 @@ public class NotificationManagerService extends SystemService {
updateInterruptionFilterLocked();
}
}
+
+ @Override
+ void onPolicyChanged() {
+ getContext().sendBroadcast(
+ new Intent(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED)
+ .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY));
+ }
});
final File systemDir = new File(Environment.getDataDirectory(), "system");
mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
@@ -1551,6 +1562,18 @@ public class NotificationManagerService extends SystemService {
message);
}
+ private void enforcePolicyToken(Policy.Token token, String method) {
+ if (!checkPolicyToken(token)) {
+ Slog.w(TAG, "Invalid notification policy token calling " + method);
+ throw new SecurityException("Invalid notification policy token");
+ }
+ }
+
+ private boolean checkPolicyToken(Policy.Token token) {
+ return mPolicyTokens.containsValue(token)
+ || mListeners.mPolicyTokens.containsValue(token);
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -1586,24 +1609,73 @@ public class NotificationManagerService extends SystemService {
enforceSystemOrSystemUIOrVolume("INotificationManager.isSystemConditionProviderEnabled");
return mConditionProviders.isSystemProviderEnabled(path);
}
- };
- private String[] getActiveNotificationKeys(INotificationListener token) {
- final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
- final ArrayList<String> keys = new ArrayList<String>();
- if (info.isEnabledForCurrentProfiles()) {
- synchronized (mNotificationList) {
- final int N = mNotificationList.size();
- for (int i = 0; i < N; i++) {
- final StatusBarNotification sbn = mNotificationList.get(i).sbn;
- if (info.enabledAndUserMatches(sbn.getUserId())) {
- keys.add(sbn.getKey());
+ @Override
+ public Policy.Token getPolicyTokenFromListener(INotificationListener listener) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mListeners.getPolicyToken(listener);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void requestNotificationPolicyToken(String pkg,
+ INotificationManagerCallback callback) throws RemoteException {
+ if (callback == null) {
+ Slog.w(TAG, "requestNotificationPolicyToken: no callback specified");
+ return;
+ }
+ if (pkg == null) {
+ Slog.w(TAG, "requestNotificationPolicyToken denied: no package specified");
+ callback.onPolicyToken(null);
+ return;
+ }
+ Policy.Token token = null;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mNotificationList) {
+ token = mPolicyTokens.get(pkg);
+ if (token == null) {
+ token = new Policy.Token(new Binder());
+ mPolicyTokens.put(pkg, token);
}
+ if (DBG) Slog.w(TAG, "requestNotificationPolicyToken granted for " + pkg);
}
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
+ callback.onPolicyToken(token);
+ }
+
+ @Override
+ public boolean isNotificationPolicyTokenValid(String pkg, Policy.Token token) {
+ return checkPolicyToken(token);
}
- return keys.toArray(new String[keys.size()]);
- }
+
+ @Override
+ public Policy getNotificationPolicy(Policy.Token token) {
+ enforcePolicyToken(token, "getNotificationPolicy");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mZenModeHelper.getNotificationPolicy();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void setNotificationPolicy(Policy.Token token, Policy policy) {
+ enforcePolicyToken(token, "setNotificationPolicy");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mZenModeHelper.setNotificationPolicy(policy);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ };
private String disableNotificationEffects(NotificationRecord record) {
if (mDisableNotificationEffects) {
@@ -1718,6 +1790,10 @@ public class NotificationManagerService extends SystemService {
pw.print(listener.component);
}
pw.println(')');
+ pw.print(" mPolicyTokens.keys: ");
+ pw.println(TextUtils.join(",", mPolicyTokens.keySet()));
+ pw.print(" mListeners.mPolicyTokens.keys: ");
+ pw.println(TextUtils.join(",", mListeners.mPolicyTokens.keySet()));
}
pw.println("\n Condition providers:");
@@ -2970,12 +3046,18 @@ public class NotificationManagerService extends SystemService {
public class NotificationListeners extends ManagedServices {
private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
+ private final ArrayMap<ComponentName, Policy.Token> mPolicyTokens = new ArrayMap<>();
private boolean mNotificationGroupsDesired;
public NotificationListeners() {
super(getContext(), mHandler, mNotificationList, mUserProfiles);
}
+ public Policy.Token getPolicyToken(INotificationListener listener) {
+ final ManagedServiceInfo info = checkServiceTokenLocked(listener);
+ return info == null ? null : mPolicyTokens.get(info.component);
+ }
+
@Override
protected Config getConfig() {
Config c = new Config();
@@ -3000,6 +3082,7 @@ public class NotificationManagerService extends SystemService {
synchronized (mNotificationList) {
updateNotificationGroupsDesiredLocked();
update = makeRankingUpdateLocked(info);
+ mPolicyTokens.put(info.component, new Policy.Token(new Binder()));
}
try {
listener.onListenerConnected(update);
@@ -3016,6 +3099,7 @@ public class NotificationManagerService extends SystemService {
}
mLightTrimListeners.remove(removed);
updateNotificationGroupsDesiredLocked();
+ mPolicyTokens.remove(removed.component);
}
public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 40218bb..9cb8af5 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -21,6 +21,7 @@ import static android.media.AudioAttributes.USAGE_NOTIFICATION;
import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
import android.app.AppOpsManager;
+import android.app.NotificationManager.Policy;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -57,6 +58,7 @@ import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Objects;
/**
* NotificationManagerService helper for functionality related to zen mode.
@@ -230,6 +232,21 @@ public class ZenModeHelper {
mConfig.writeXml(out);
}
+ public Policy getNotificationPolicy() {
+ return getNotificationPolicy(mConfig);
+ }
+
+ private static Policy getNotificationPolicy(ZenModeConfig config) {
+ return config == null ? null : config.toNotificationPolicy();
+ }
+
+ public void setNotificationPolicy(Policy policy) {
+ if (policy == null || mConfig == null) return;
+ final ZenModeConfig newConfig = mConfig.copy();
+ newConfig.applyNotificationPolicy(policy);
+ setConfig(newConfig, "setNotificationPolicy");
+ }
+
public ZenModeConfig getConfig() {
return mConfig;
}
@@ -247,8 +264,13 @@ public class ZenModeHelper {
if (config.equals(mConfig)) return true;
if (DEBUG) Log.d(TAG, "setConfig reason=" + reason);
ZenLog.traceConfig(mConfig, config);
+ final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig),
+ getNotificationPolicy(config));
mConfig = config;
dispatchOnConfigChanged();
+ if (policyChanged){
+ dispatchOnPolicyChanged();
+ }
final String val = Integer.toString(mConfig.hashCode());
Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
if (!evaluateZenMode(reason, setRingerMode)) {
@@ -355,6 +377,12 @@ public class ZenModeHelper {
}
}
+ private void dispatchOnPolicyChanged() {
+ for (Callback callback : mCallbacks) {
+ callback.onPolicyChanged();
+ }
+ }
+
private void dispatchOnZenModeChanged() {
for (Callback callback : mCallbacks) {
callback.onZenModeChanged();
@@ -617,6 +645,7 @@ public class ZenModeHelper {
public static class Callback {
void onConfigChanged() {}
void onZenModeChanged() {}
+ void onPolicyChanged() {}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a260b0e..c12545b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6280,7 +6280,8 @@ public class PackageManagerService extends IPackageManager.Stub {
!VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) {
final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;
for (int userId : userIds) {
- if (mInstaller.linkNativeLibraryDirectory(pkg.packageName, nativeLibPath, userId) < 0) {
+ if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
+ nativeLibPath, userId) < 0) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Failed linking native library dir (user=" + userId + ")");
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 936840a..fce01e5 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -183,6 +183,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
static final int APPLICATION_PANEL_SUBLAYER = 1;
static final int APPLICATION_SUB_PANEL_SUBLAYER = 2;
+ static final int APPLICATION_ABOVE_SUB_PANEL_SUBLAYER = 3;
static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
@@ -2015,6 +2016,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return APPLICATION_MEDIA_OVERLAY_SUBLAYER;
case TYPE_APPLICATION_SUB_PANEL:
return APPLICATION_SUB_PANEL_SUBLAYER;
+ case TYPE_APPLICATION_ABOVE_SUB_PANEL:
+ return APPLICATION_ABOVE_SUB_PANEL_SUBLAYER;
}
Log.e(TAG, "Unknown sub-window type: " + type);
return 0;
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 75c33af..1a52933 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -145,6 +145,9 @@ public class KeyguardServiceDelegate {
if (mKeyguardState.bootCompleted) {
mKeyguardService.onBootCompleted();
}
+ if (mKeyguardState.occluded) {
+ mKeyguardService.setOccluded(mKeyguardState.occluded);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index f6df757..184224b 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -79,7 +79,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
public void binderDied() {
Slog.i(TAG, "binder died for pkg=" + pkg);
- disableInternal(userId, 0, token, pkg);
+ disableForUser(0, token, pkg, userId);
token.unlinkToDeath(this, 0);
}
}
@@ -194,10 +194,11 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
@Override
public void disable(int what, IBinder token, String pkg) {
- disableInternal(mCurrentUserId, what, token, pkg);
+ disableForUser(what, token, pkg, mCurrentUserId);
}
- private void disableInternal(int userId, int what, IBinder token, String pkg) {
+ @Override
+ public void disableForUser(int what, IBinder token, String pkg, int userId) {
enforceStatusBar();
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index d4c5f87..ac79b36 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -40,6 +40,8 @@ public class WebViewUpdateService extends SystemService {
private boolean mRelroReady32Bit = false;
private boolean mRelroReady64Bit = false;
+ private String oldWebViewPackageName = null;
+
private BroadcastReceiver mWebViewUpdatedReceiver;
public WebViewUpdateService(Context context) {
@@ -51,9 +53,22 @@ public class WebViewUpdateService extends SystemService {
mWebViewUpdatedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- String webviewPackage = "package:" + WebViewFactory.getWebViewPackageName();
- if (webviewPackage.equals(intent.getDataString())) {
- onWebViewUpdateInstalled();
+
+ for (String packageName : WebViewFactory.getWebViewPackageNames()) {
+ String webviewPackage = "package:" + packageName;
+
+ if (webviewPackage.equals(intent.getDataString())) {
+ String usedPackageName =
+ WebViewFactory.findPreferredWebViewPackage().packageName;
+ // Only trigger update actions if the updated package is the one that
+ // will be used, or the one that was in use before the update.
+ if (packageName.equals(usedPackageName) ||
+ packageName.equals(oldWebViewPackageName)) {
+ onWebViewUpdateInstalled();
+ oldWebViewPackageName = usedPackageName;
+ }
+ return;
+ }
}
}
};
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index ae8832a..91ce739 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -339,6 +339,7 @@ final class AccessibilityController {
case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL:
case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
case WindowManager.LayoutParams.TYPE_PHONE:
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index a04f6cb..e914cd4 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -56,7 +56,7 @@ class AppWindowToken extends WindowToken {
boolean appFullscreen;
int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
boolean layoutConfigChanges;
- boolean showWhenLocked;
+ boolean showForAllUsers;
// The input dispatching timeout for this application token in nanoseconds.
long inputDispatchingTimeoutNanos;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index f073c23..f914369 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -66,6 +66,7 @@ class DisplayContent {
int mBaseDisplayWidth = 0;
int mBaseDisplayHeight = 0;
int mBaseDisplayDensity = 0;
+ boolean mDisplayScalingDisabled;
private final DisplayInfo mDisplayInfo = new DisplayInfo();
private final Display mDisplay;
@@ -360,6 +361,9 @@ class DisplayContent {
pw.print(mBaseDisplayWidth); pw.print("x"); pw.print(mBaseDisplayHeight);
pw.print(" "); pw.print(mBaseDisplayDensity); pw.print("dpi");
}
+ if (mDisplayScalingDisabled) {
+ pw.println(" noscale");
+ }
pw.print(" cur=");
pw.print(mDisplayInfo.logicalWidth);
pw.print("x"); pw.print(mDisplayInfo.logicalHeight);
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 55dd911..bb53534 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -148,7 +148,7 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
if (timeout >= 0) {
// The activity manager declined to abort dispatching.
// Wait a bit longer and timeout again later.
- return timeout;
+ return timeout * 1000000L; // nanoseconds
}
} catch (RemoteException ex) {
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 34120a0..0c3cf65 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -107,9 +107,9 @@ class Task {
}
}
- boolean showWhenLocked() {
+ boolean showForAllUsers() {
final int tokensCount = mAppTokens.size();
- return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showWhenLocked;
+ return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showForAllUsers;
}
@Override
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index e845f83..7cdf8b2 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -276,27 +276,26 @@ public class TaskStack {
}
void addTask(Task task, boolean toTop) {
- addTask(task, toTop, task.showWhenLocked());
+ addTask(task, toTop, task.showForAllUsers());
}
/**
* Put a Task in this stack. Used for adding and moving.
* @param task The task to add.
* @param toTop Whether to add it to the top or bottom.
- * @param showWhenLocked Whether to show the task when the device is locked
- * regardless of the current user.
+ * @param showForAllUsers Whether to show the task regardless of the current user.
*/
- void addTask(Task task, boolean toTop, boolean showWhenLocked) {
+ void addTask(Task task, boolean toTop, boolean showForAllUsers) {
int stackNdx;
if (!toTop) {
stackNdx = 0;
} else {
stackNdx = mTasks.size();
- if (!showWhenLocked && !mService.isCurrentProfileLocked(task.mUserId)) {
+ if (!showForAllUsers && !mService.isCurrentProfileLocked(task.mUserId)) {
// Place the task below all current user tasks.
while (--stackNdx >= 0) {
final Task tmpTask = mTasks.get(stackNdx);
- if (!tmpTask.showWhenLocked()
+ if (!tmpTask.showForAllUsers()
|| !mService.isCurrentProfileLocked(tmpTask.mUserId)) {
break;
}
@@ -498,7 +497,7 @@ public class TaskStack {
int top = mTasks.size();
for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
Task task = mTasks.get(taskNdx);
- if (mService.isCurrentProfileLocked(task.mUserId) || task.showWhenLocked()) {
+ if (mService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) {
mTasks.remove(taskNdx);
mTasks.add(task);
--top;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e790fb0..964dcc2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3620,13 +3620,13 @@ public class WindowManagerService extends IWindowManager.Stub
EventLog.writeEvent(EventLogTags.WM_TASK_CREATED, taskId, stackId);
Task task = new Task(taskId, stack, userId, this);
mTaskIdToTask.put(taskId, task);
- stack.addTask(task, !atoken.mLaunchTaskBehind /* toTop */, atoken.showWhenLocked);
+ stack.addTask(task, !atoken.mLaunchTaskBehind /* toTop */, atoken.showForAllUsers);
return task;
}
@Override
public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
- int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId,
+ int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int userId,
int configChanges, boolean voiceInteraction, boolean launchTaskBehind) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"addAppToken()")) {
@@ -3656,7 +3656,7 @@ public class WindowManagerService extends IWindowManager.Stub
atoken = new AppWindowToken(this, token, voiceInteraction);
atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
atoken.appFullscreen = fullscreen;
- atoken.showWhenLocked = showWhenLocked;
+ atoken.showForAllUsers = showForAllUsers;
atoken.requestedOrientation = requestedOrientation;
atoken.layoutConfigChanges = (configChanges &
(ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) != 0;
@@ -7249,8 +7249,15 @@ public class WindowManagerService extends IWindowManager.Stub
displayInfo.getLogicalMetrics(mRealDisplayMetrics,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
displayInfo.getAppMetrics(mDisplayMetrics);
+ if (displayContent.mDisplayScalingDisabled) {
+ displayInfo.flags |= Display.FLAG_SCALING_DISABLED;
+ } else {
+ displayInfo.flags &= ~Display.FLAG_SCALING_DISABLED;
+ }
+
mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
displayContent.getDisplayId(), displayInfo);
+
displayContent.mBaseDisplayRect.set(0, 0, dw, dh);
}
if (false) {
@@ -7547,7 +7554,7 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized(mWindowMap) {
final DisplayContent displayContent = getDefaultDisplayContentLocked();
- readForcedDisplaySizeAndDensityLocked(displayContent);
+ readForcedDisplayPropertiesLocked(displayContent);
mDisplayReady = true;
}
@@ -8320,7 +8327,47 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- private void readForcedDisplaySizeAndDensityLocked(final DisplayContent displayContent) {
+ @Override
+ public void setForcedDisplayScalingMode(int displayId, int mode) {
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
+ PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Must hold permission " +
+ android.Manifest.permission.WRITE_SECURE_SETTINGS);
+ }
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ throw new IllegalArgumentException("Can only set the default display");
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized(mWindowMap) {
+ final DisplayContent displayContent = getDisplayContentLocked(displayId);
+ if (displayContent != null) {
+ if (mode < 0 || mode > 1) {
+ mode = 0;
+ }
+ setForcedDisplayScalingModeLocked(displayContent, mode);
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.DISPLAY_SCALING_FORCE, mode);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void setForcedDisplayScalingModeLocked(DisplayContent displayContent,
+ int mode) {
+ Slog.i(TAG, "Using display scaling mode: " + (mode == 0 ? "auto" : "off"));
+
+ synchronized(displayContent.mDisplaySizeLock) {
+ displayContent.mDisplayScalingDisabled = (mode != 0);
+ }
+ reconfigureDisplayLocked(displayContent);
+ }
+
+ private void readForcedDisplayPropertiesLocked(final DisplayContent displayContent) {
+ // Display size.
String sizeStr = Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.DISPLAY_SIZE_FORCED);
if (sizeStr == null || sizeStr.length() == 0) {
@@ -8345,6 +8392,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
}
+
+ // Display density.
String densityStr = Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.DISPLAY_DENSITY_FORCED);
if (densityStr == null || densityStr.length() == 0) {
@@ -8363,6 +8412,16 @@ public class WindowManagerService extends IWindowManager.Stub
} catch (NumberFormatException ex) {
}
}
+
+ // Display scaling mode.
+ int mode = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.DISPLAY_SCALING_FORCE, 0);
+ if (mode != 0) {
+ synchronized(displayContent.mDisplaySizeLock) {
+ Slog.i(TAG, "FORCED DISPLAY SCALING DISABLED");
+ displayContent.mDisplayScalingDisabled = true;
+ }
+ }
}
// displayContent must not be null
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index db3268d..4f795a6 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
@@ -478,6 +479,12 @@ final class WindowState implements WindowManagerPolicy.WindowState {
if (mAppToken != null) {
final DisplayContent appDisplay = getDisplayContent();
mNotOnAppsDisplay = displayContent != appDisplay;
+
+ if (mAppToken.showForAllUsers) {
+ // Windows for apps that can show for all users should also show when the
+ // device is locked.
+ mAttrs.flags |= FLAG_SHOW_WHEN_LOCKED;
+ }
}
mWinAnimator = new WindowStateAnimator(this);
@@ -1353,7 +1360,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
win = win.mAttachedWindow;
}
if (win.mAttrs.type < WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW
- && win.mAppToken != null && win.mAppToken.showWhenLocked) {
+ && win.mAppToken != null && win.mAppToken.showForAllUsers) {
// Save some cycles by not calling getDisplayInfo unless it is an application
// window intended for all users.
final DisplayContent displayContent = win.getDisplayContent();