diff options
Diffstat (limited to 'services/core/java')
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(); |