diff options
67 files changed, 946 insertions, 737 deletions
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index 8c84b4d..9ef13de 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -426,7 +426,7 @@ public class AccountManager { @RequiresPermission(GET_ACCOUNTS) public Account[] getAccounts() { try { - return mService.getAccounts(null); + return mService.getAccounts(null, mContext.getOpPackageName()); } catch (RemoteException e) { // won't ever happen throw new RuntimeException(e); @@ -451,7 +451,7 @@ public class AccountManager { @RequiresPermission(GET_ACCOUNTS) public Account[] getAccountsAsUser(int userId) { try { - return mService.getAccountsAsUser(null, userId); + return mService.getAccountsAsUser(null, userId, mContext.getOpPackageName()); } catch (RemoteException e) { // won't ever happen throw new RuntimeException(e); @@ -468,7 +468,7 @@ public class AccountManager { */ public Account[] getAccountsForPackage(String packageName, int uid) { try { - return mService.getAccountsForPackage(packageName, uid); + return mService.getAccountsForPackage(packageName, uid, mContext.getOpPackageName()); } catch (RemoteException re) { // won't ever happen throw new RuntimeException(re); @@ -485,7 +485,8 @@ public class AccountManager { */ public Account[] getAccountsByTypeForPackage(String type, String packageName) { try { - return mService.getAccountsByTypeForPackage(type, packageName); + return mService.getAccountsByTypeForPackage(type, packageName, + mContext.getOpPackageName()); } catch (RemoteException re) { // won't ever happen throw new RuntimeException(re); @@ -522,7 +523,8 @@ public class AccountManager { /** @hide Same as {@link #getAccountsByType(String)} but for a specific user. */ public Account[] getAccountsByTypeAsUser(String type, UserHandle userHandle) { try { - return mService.getAccountsAsUser(type, userHandle.getIdentifier()); + return mService.getAccountsAsUser(type, userHandle.getIdentifier(), + mContext.getOpPackageName()); } catch (RemoteException e) { // won't ever happen throw new RuntimeException(e); @@ -610,7 +612,7 @@ public class AccountManager { if (features == null) throw new IllegalArgumentException("features is null"); return new Future2Task<Boolean>(handler, callback) { public void doWork() throws RemoteException { - mService.hasFeatures(mResponse, account, features); + mService.hasFeatures(mResponse, account, features, mContext.getOpPackageName()); } public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException { if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) { @@ -662,7 +664,8 @@ public class AccountManager { if (type == null) throw new IllegalArgumentException("type is null"); return new Future2Task<Account[]>(handler, callback) { public void doWork() throws RemoteException { - mService.getAccountsByFeatures(mResponse, type, features); + mService.getAccountsByFeatures(mResponse, type, features, + mContext.getOpPackageName()); } public Account[] bundleToResult(Bundle bundle) throws AuthenticatorException { if (!bundle.containsKey(KEY_ACCOUNTS)) { diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl index 04b3c88..4378df4 100644 --- a/core/java/android/accounts/IAccountManager.aidl +++ b/core/java/android/accounts/IAccountManager.aidl @@ -30,12 +30,14 @@ interface IAccountManager { String getPassword(in Account account); String getUserData(in Account account, String key); AuthenticatorDescription[] getAuthenticatorTypes(int userId); - Account[] getAccounts(String accountType); - Account[] getAccountsForPackage(String packageName, int uid); - Account[] getAccountsByTypeForPackage(String type, String packageName); - Account[] getAccountsAsUser(String accountType, int userId); - void hasFeatures(in IAccountManagerResponse response, in Account account, in String[] features); - void getAccountsByFeatures(in IAccountManagerResponse response, String accountType, in String[] features); + Account[] getAccounts(String accountType, String opPackageName); + Account[] getAccountsForPackage(String packageName, int uid, String opPackageName); + Account[] getAccountsByTypeForPackage(String type, String packageName, String opPackageName); + Account[] getAccountsAsUser(String accountType, int userId, String opPackageName); + void hasFeatures(in IAccountManagerResponse response, in Account account, in String[] features, + String opPackageName); + void getAccountsByFeatures(in IAccountManagerResponse response, String accountType, + in String[] features, String opPackageName); boolean addAccountExplicitly(in Account account, String password, in Bundle extras); void removeAccount(in IAccountManagerResponse response, in Account account, boolean expectActivityLaunch); diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 42ac67c..09c0a6e 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -235,8 +235,10 @@ public class AppOpsManager { public static final int OP_WRITE_EXTERNAL_STORAGE = 60; /** @hide Turned on the screen. */ public static final int OP_TURN_SCREEN_ON = 61; + /** @hide Get device accounts. */ + public static final int OP_GET_ACCOUNTS = 62; /** @hide */ - public static final int _NUM_OP = 62; + public static final int _NUM_OP = 63; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -331,6 +333,9 @@ public class AppOpsManager { /** Required to write/modify/update system settingss. */ public static final String OPSTR_WRITE_SETTINGS = "android:write_settings"; + /** @hide Get device accounts. */ + public static final String OPSTR_GET_ACCOUNTS + = "android:get_accounts"; /** * This maps each operation to the operation that serves as the @@ -403,6 +408,7 @@ public class AppOpsManager { OP_READ_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE, OP_TURN_SCREEN_ON, + OP_GET_ACCOUNTS, }; /** @@ -472,6 +478,7 @@ public class AppOpsManager { OPSTR_READ_EXTERNAL_STORAGE, OPSTR_WRITE_EXTERNAL_STORAGE, null, + OPSTR_GET_ACCOUNTS }; /** @@ -541,6 +548,7 @@ public class AppOpsManager { "READ_EXTERNAL_STORAGE", "WRITE_EXTERNAL_STORAGE", "TURN_ON_SCREEN", + "GET_ACCOUNTS", }; /** @@ -610,6 +618,7 @@ public class AppOpsManager { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, null, // no permission for turning the screen on + Manifest.permission.GET_ACCOUNTS }; /** @@ -680,6 +689,7 @@ public class AppOpsManager { null, // READ_EXTERNAL_STORAGE null, // WRITE_EXTERNAL_STORAGE null, // TURN_ON_SCREEN + null, // GET_ACCOUNTS }; /** @@ -749,6 +759,7 @@ public class AppOpsManager { false, // READ_EXTERNAL_STORAGE false, // WRITE_EXTERNAL_STORAGE false, // TURN_ON_SCREEN + false, // GET_ACCOUNTS }; /** @@ -817,6 +828,7 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, // OP_TURN_ON_SCREEN + AppOpsManager.MODE_ALLOWED, }; /** @@ -889,6 +901,7 @@ public class AppOpsManager { false, false, false, + false }; /** diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 1f23c0a..7fef5e1 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -27,6 +27,7 @@ import android.os.CancellationSignal.OnCancelListener; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.PowerManager; import android.os.RemoteException; import android.os.UserHandle; import android.security.keystore.AndroidKeyStoreProvider; @@ -705,15 +706,23 @@ public class FingerprintManager { public void addLockoutResetCallback(final LockoutResetCallback callback) { if (mService != null) { try { + final PowerManager powerManager = mContext.getSystemService(PowerManager.class); mService.addLockoutResetCallback( new IFingerprintServiceLockoutResetCallback.Stub() { @Override public void onLockoutReset(long deviceId) throws RemoteException { + final PowerManager.WakeLock wakeLock = powerManager.newWakeLock( + PowerManager.PARTIAL_WAKE_LOCK, "lockoutResetCallback"); + wakeLock.acquire(); mHandler.post(new Runnable() { @Override public void run() { - callback.onLockoutReset(); + try { + callback.onLockoutReset(); + } finally { + wakeLock.release(); + } } }); } diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl index c9a5d59..e027a2b 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl @@ -23,6 +23,8 @@ import android.os.UserHandle; * Callback when lockout period expired and clients are allowed to authenticate again. * @hide */ -oneway interface IFingerprintServiceLockoutResetCallback { +interface IFingerprintServiceLockoutResetCallback { + + /** Method is synchronous so wakelock is held when this is called from a WAKEUP alarm. */ void onLockoutReset(long deviceId); } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 4055836..9a2a241 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -1243,6 +1243,8 @@ public class ConnectivityManager { /** The hardware does not support this request. */ public static final int ERROR_HARDWARE_UNSUPPORTED = -30; + /** The hardware returned an error. */ + public static final int ERROR_HARDWARE_ERROR = -31; public static final int NATT_PORT = 4500; diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java index 5f46c73..cab88b9 100644 --- a/core/java/android/net/NetworkFactory.java +++ b/core/java/android/net/NetworkFactory.java @@ -169,7 +169,8 @@ public class NetworkFactory extends Handler { } } - private void handleAddRequest(NetworkRequest request, int score) { + @VisibleForTesting + protected void handleAddRequest(NetworkRequest request, int score) { NetworkRequestInfo n = mNetworkRequests.get(request.requestId); if (n == null) { if (DBG) log("got request " + request + " with score " + score); @@ -184,7 +185,8 @@ public class NetworkFactory extends Handler { evalRequest(n); } - private void handleRemoveRequest(NetworkRequest request) { + @VisibleForTesting + protected void handleRemoveRequest(NetworkRequest request) { NetworkRequestInfo n = mNetworkRequests.get(request.requestId); if (n != null) { mNetworkRequests.remove(request.requestId); diff --git a/core/java/android/nfc/cardemulation/AidGroup.java b/core/java/android/nfc/cardemulation/AidGroup.java index 4407c9d..78a9401 100644 --- a/core/java/android/nfc/cardemulation/AidGroup.java +++ b/core/java/android/nfc/cardemulation/AidGroup.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package android.nfc.cardemulation; import java.io.IOException; diff --git a/core/java/android/nfc/cardemulation/HostApduService.java b/core/java/android/nfc/cardemulation/HostApduService.java index ad34e61..a299479 100644 --- a/core/java/android/nfc/cardemulation/HostApduService.java +++ b/core/java/android/nfc/cardemulation/HostApduService.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package android.nfc.cardemulation; import android.annotation.SdkConstant; diff --git a/core/java/android/nfc/cardemulation/OffHostApduService.java b/core/java/android/nfc/cardemulation/OffHostApduService.java index 0d01762..6a8aeee 100644 --- a/core/java/android/nfc/cardemulation/OffHostApduService.java +++ b/core/java/android/nfc/cardemulation/OffHostApduService.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package android.nfc.cardemulation; import android.annotation.SdkConstant; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 78be3cd..2053dbe 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5719,6 +5719,15 @@ public final class Settings { public static final String CAMERA_GESTURE_DISABLED = "camera_gesture_disabled"; /** + * Whether the camera launch gesture to double tap the power button when the screen is off + * should be disabled. + * + * @hide + */ + public static final String CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED = + "camera_double_tap_power_gesture_disabled"; + + /** * This are the settings to be backed up. * * NOTE: Settings are backed up and restored in the order they appear diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java index 6da0f63..b6240e4 100644 --- a/core/java/com/android/internal/logging/MetricsLogger.java +++ b/core/java/com/android/internal/logging/MetricsLogger.java @@ -44,6 +44,8 @@ public class MetricsLogger implements MetricsConstants { public static final int ACTION_FINGERPRINT_AUTH = 252; public static final int ACTION_FINGERPRINT_DELETE = 253; public static final int ACTION_FINGERPRINT_RENAME = 254; + public static final int ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE = 255; + public static final int ACTION_WIGGLE_CAMERA_GESTURE = 256; public static void visible(Context context, int category) throws IllegalArgumentException { if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) { diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 4ac2958..a873ef8 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -1174,7 +1174,7 @@ public class LockPatternUtils { * @param userId either an explicit user id or {@link android.os.UserHandle#USER_ALL} */ public void requireCredentialEntry(int userId) { - requireStrongAuth(StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_REQUEST, userId); + requireStrongAuth(StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, userId); } /** @@ -1251,7 +1251,7 @@ public class LockPatternUtils { value = { STRONG_AUTH_NOT_REQUIRED, STRONG_AUTH_REQUIRED_AFTER_BOOT, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW, - STRONG_AUTH_REQUIRED_AFTER_USER_REQUEST}) + SOME_AUTH_REQUIRED_AFTER_USER_REQUEST}) @Retention(RetentionPolicy.SOURCE) public @interface StrongAuthFlags {} @@ -1266,14 +1266,14 @@ public class LockPatternUtils { public static final int STRONG_AUTH_REQUIRED_AFTER_BOOT = 0x1; /** - * Strong authentication is required because a device admin has temporarily requested it. + * Strong authentication is required because a device admin has requested it. */ public static final int STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW = 0x2; /** - * Strong authentication is required because the user has temporarily requested it. + * Some authentication is required because the user has temporarily disabled trust. */ - public static final int STRONG_AUTH_REQUIRED_AFTER_USER_REQUEST = 0x4; + public static final int SOME_AUTH_REQUIRED_AFTER_USER_REQUEST = 0x4; /** * Strong authentication is required because the user has been locked out after too many @@ -1289,6 +1289,7 @@ public class LockPatternUtils { public static final int DEFAULT = STRONG_AUTH_REQUIRED_AFTER_BOOT; private static final int ALLOWING_FINGERPRINT = STRONG_AUTH_NOT_REQUIRED + | SOME_AUTH_REQUIRED_AFTER_USER_REQUEST | SOME_AUTH_REQUIRED_AFTER_WRONG_CREDENTIAL; private final SparseIntArray mStrongAuthRequiredForUser = new SparseIntArray(); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index c8bb675..b8b6444 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1056,6 +1056,11 @@ <permission android:name="android.permission.CONNECTIVITY_INTERNAL" android:protectionLevel="signature|privileged" /> + <!-- Allows a system application to access hardware packet offload capabilities. + @hide --> + <permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" + android:protectionLevel="signature|privileged" /> + <!-- @SystemApi @hide --> <permission android:name="android.permission.RECEIVE_DATA_ACTIVITY_CHANGE" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index d7658d1..fd600e3 100755..100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2285,9 +2285,16 @@ <string-array name="config_cell_retries_per_error_code"> </string-array> + <!-- Set initial MaxRetry value for operators --> + <integer name="config_mdc_initial_max_retry">1</integer> + <!-- The OEM specified sensor type for the gesture to launch the camear app. --> <integer name="config_cameraLaunchGestureSensorType">-1</integer> <!-- The OEM specified sensor string type for the gesture to launch camera app, this value must match the value of config_cameraLaunchGestureSensorType in OEM's HAL --> <string translatable="false" name="config_cameraLaunchGestureSensorStringType"></string> + + <!-- Allow the gesture to double tap the power button twice to start the camera while the device + is non-interactive. --> + <bool name="config_cameraDoubleTapPowerGestureEnabled">true</bool> </resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 09c1e6f..8635a4f 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -37,7 +37,7 @@ <!-- Height of the bottom navigation bar in portrait; often the same as @dimen/navigation_bar_height --> <dimen name="navigation_bar_height_landscape">48dp</dimen> <!-- Width of the navigation bar when it is placed vertically on the screen --> - <dimen name="navigation_bar_width">42dp</dimen> + <dimen name="navigation_bar_width">48dp</dimen> <!-- Height of notification icons in the status bar --> <dimen name="status_bar_icon_size">24dip</dimen> <!-- Size of the giant number (unread count) in the notifications --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 4c8e55e..357d4c3 100755..100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -404,6 +404,7 @@ <java-symbol type="integer" name="config_volte_replacement_rat"/> <java-symbol type="integer" name="config_valid_wappush_index" /> <java-symbol type="integer" name="config_overrideHasPermanentMenuKey" /> + <java-symbol type="integer" name="config_mdc_initial_max_retry" /> <java-symbol type="bool" name="config_hasPermanentDpad" /> <java-symbol type="color" name="tab_indicator_text_v4" /> @@ -2323,6 +2324,7 @@ <!-- Gesture --> <java-symbol type="integer" name="config_cameraLaunchGestureSensorType" /> <java-symbol type="string" name="config_cameraLaunchGestureSensorStringType" /> + <java-symbol type="bool" name="config_cameraDoubleTapPowerGestureEnabled" /> <java-symbol type="drawable" name="platlogo_m" /> </resources> diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java index b752c9b..47d8e28 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -1032,7 +1032,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } private boolean shouldListenForFingerprint() { - return (mKeyguardIsVisible || !mDeviceInteractive) && !mSwitchingUser + return (mKeyguardIsVisible || !mDeviceInteractive || mBouncer) && !mSwitchingUser && !mFingerprintAlreadyAuthenticated && !isFingerprintDisabled(getCurrentUser()); } @@ -1365,6 +1365,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { cb.onKeyguardBouncerChanged(isBouncer); } } + updateFingerprintListeningState(); } /** diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_land.png Binary files differdeleted file mode 100644 index 165ef4f..0000000 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png Binary files differdeleted file mode 100644 index f95f09f..0000000 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png Binary files differdeleted file mode 100644 index 860a906..0000000 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_land.png Binary files differdeleted file mode 100644 index bcb203e..0000000 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png Binary files differdeleted file mode 100644 index bab268e..0000000 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_land.png Binary files differdeleted file mode 100644 index 2f4dbbe..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_land.png Binary files differdeleted file mode 100644 index d04d84f5..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_land.png Binary files differdeleted file mode 100644 index 1500ae5..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_land.png Binary files differdeleted file mode 100644 index a7fec49..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_land.png Binary files differdeleted file mode 100644 index 0feb405..0000000 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png Binary files differdeleted file mode 100644 index cabab0d..0000000 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png Binary files differdeleted file mode 100644 index 16e1bf5..0000000 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_land.png Binary files differdeleted file mode 100644 index 94c9743..0000000 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png Binary files differdeleted file mode 100644 index 40375de..0000000 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_land.png Binary files differdeleted file mode 100644 index b7b8f98..0000000 --- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png Binary files differdeleted file mode 100644 index 69b7449..0000000 --- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png Binary files differdeleted file mode 100644 index 57d243c..0000000 --- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_land.png Binary files differdeleted file mode 100644 index 8a7ac4f..0000000 --- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png Binary files differdeleted file mode 100644 index e53eaff..0000000 --- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_land.png Binary files differdeleted file mode 100644 index 695e7a4..0000000 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png Binary files differdeleted file mode 100644 index 88294c0..0000000 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png Binary files differdeleted file mode 100644 index 09d684a..0000000 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu_land.png Binary files differdeleted file mode 100644 index 62f44e8..0000000 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png Binary files differdeleted file mode 100644 index e31ea32..0000000 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_land.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_land.png Binary files differdeleted file mode 100644 index 24f12d7..0000000 --- a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_land.png Binary files differdeleted file mode 100644 index 51482f5..0000000 --- a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_land.png Binary files differdeleted file mode 100644 index 46c7b18..0000000 --- a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_recent_land.png Binary files differdeleted file mode 100644 index 396ad7d..0000000 --- a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_recent_land.png +++ /dev/null diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml index c92ba45..d58664f 100644 --- a/packages/SystemUI/res/layout/navigation_bar.xml +++ b/packages/SystemUI/res/layout/navigation_bar.xml @@ -213,7 +213,7 @@ android:layout_width="match_parent" android:layout_height="40dp" android:contentDescription="@string/accessibility_menu" - android:src="@drawable/ic_sysbar_menu_land" + android:src="@drawable/ic_sysbar_menu" android:scaleType="centerInside" android:layout_gravity="top" android:visibility="invisible" @@ -223,7 +223,7 @@ <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/recent_apps" android:layout_height="@dimen/navigation_key_width" android:layout_width="match_parent" - android:src="@drawable/ic_sysbar_recent_land" + android:src="@drawable/ic_sysbar_recent" android:scaleType="center" android:layout_weight="0" android:contentDescription="@string/accessibility_recent" @@ -237,7 +237,7 @@ <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home" android:layout_height="@dimen/navigation_key_width" android:layout_width="match_parent" - android:src="@drawable/ic_sysbar_home_land" + android:src="@drawable/ic_sysbar_home" android:scaleType="center" systemui:keyCode="3" systemui:keyRepeat="false" @@ -253,7 +253,7 @@ <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back" android:layout_height="@dimen/navigation_key_width" android:layout_width="match_parent" - android:src="@drawable/ic_sysbar_back_land" + android:src="@drawable/ic_sysbar_back" android:scaleType="center" systemui:keyCode="4" android:layout_weight="0" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index f4439bf..012dc9c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -390,7 +390,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL serviceIntent.setAction(CameraPrewarmService.ACTION_PREWARM); try { if (getContext().bindServiceAsUser(serviceIntent, mPrewarmConnection, - Context.BIND_AUTO_CREATE, new UserHandle(UserHandle.USER_CURRENT))) { + Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, + new UserHandle(UserHandle.USER_CURRENT))) { mPrewarmBound = true; } } catch (SecurityException e) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java index 030501b..8e58d14 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -166,13 +166,10 @@ public class LockIcon extends KeyguardAffordanceView { if (mAccessibilityController == null) { return; } - boolean trustManagedOrFingerprintAllowed = mUnlockMethodCache.isTrustManaged() - || KeyguardUpdateMonitor.getInstance(mContext).isUnlockingWithFingerprintAllowed(); - boolean clickToUnlock = mAccessibilityController.isTouchExplorationEnabled(); - boolean clickToForceLock = trustManagedOrFingerprintAllowed + boolean clickToForceLock = mUnlockMethodCache.isTrustManaged() && !mAccessibilityController.isAccessibilityEnabled(); - boolean longClickToForceLock = trustManagedOrFingerprintAllowed + boolean longClickToForceLock = mUnlockMethodCache.isTrustManaged() && !clickToForceLock; setClickable(clickToForceLock || clickToUnlock); setLongClickable(longClickToForceLock); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index cc31476..8046eb5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -257,11 +257,11 @@ public class NavigationBarView extends LinearLayout { private void getIcons(Resources res) { mBackIcon = res.getDrawable(R.drawable.ic_sysbar_back); - mBackLandIcon = res.getDrawable(R.drawable.ic_sysbar_back_land); + mBackLandIcon = mBackIcon; mBackAltIcon = res.getDrawable(R.drawable.ic_sysbar_back_ime); - mBackAltLandIcon = res.getDrawable(R.drawable.ic_sysbar_back_ime_land); + mBackAltLandIcon = mBackAltIcon; mRecentIcon = res.getDrawable(R.drawable.ic_sysbar_recent); - mRecentLandIcon = res.getDrawable(R.drawable.ic_sysbar_recent_land); + mRecentLandIcon = mRecentIcon; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index b93586e..9321938 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -111,12 +111,6 @@ public class NotificationPanelView extends PanelView implements private boolean mQsTracking; /** - * Handles launching the secure camera properly even when other applications may be using the - * camera hardware. - */ - private SecureCameraLaunchManager mSecureCameraLaunchManager; - - /** * If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and * the expansion for quick settings. */ @@ -262,8 +256,6 @@ public class NotificationPanelView extends PanelView implements mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area); mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim); mAfforanceHelper = new KeyguardAffordanceHelper(this, getContext()); - mSecureCameraLaunchManager = - new SecureCameraLaunchManager(getContext(), mKeyguardBottomArea); mLastOrientation = getResources().getConfiguration().orientation; // recompute internal state when qspanel height changes @@ -368,16 +360,6 @@ public class NotificationPanelView extends PanelView implements updateMaxHeadsUpTranslation(); } - @Override - public void onAttachedToWindow() { - mSecureCameraLaunchManager.create(); - } - - @Override - public void onDetachedFromWindow() { - mSecureCameraLaunchManager.destroy(); - } - private void startQsSizeChangeAnimation(int oldHeight, final int newHeight) { if (mQsSizeChangeAnimator != null) { oldHeight = (int) mQsSizeChangeAnimator.getAnimatedValue(); @@ -1963,7 +1945,7 @@ public class NotificationPanelView extends PanelView implements } else { EventLogTags.writeSysuiLockscreenGesture( EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_CAMERA, lengthDp, velocityDp); - mSecureCameraLaunchManager.startSecureCameraLaunch(); + mKeyguardBottomArea.launchCamera(); } mStatusBar.startLaunchTransitionTimeout(); mBlockTouches = true; @@ -2010,7 +1992,6 @@ public class NotificationPanelView extends PanelView implements boolean camera = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? !rightIcon : rightIcon; if (camera) { - mSecureCameraLaunchManager.onSwipingStarted(); mKeyguardBottomArea.bindCameraPrewarmService(); } requestDisallowInterceptTouchEvent(true); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 55b1127..548125d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -289,6 +289,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private DozeServiceHost mDozeServiceHost; private boolean mWakeUpComingFromTouch; private PointF mWakeUpTouchLocation; + private boolean mScreenTurningOn; int mPixelFormat; Object mQueueLock = new Object(); @@ -3535,6 +3536,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); releaseGestureWakeLock(); mNotificationPanel.onAffordanceLaunchEnded(); + mNotificationPanel.animate().cancel(); mNotificationPanel.setAlpha(1f); return staying; } @@ -3920,6 +3922,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } public void onScreenTurningOn() { + mScreenTurningOn = true; mNotificationPanel.onScreenTurningOn(); if (mLaunchCameraOnScreenTurningOn) { mNotificationPanel.launchCamera(false); @@ -3928,10 +3931,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } private void vibrateForCameraGesture() { - mVibrator.vibrate(1000L); + mVibrator.vibrate(750L); } public void onScreenTurnedOn() { + mScreenTurningOn = false; mDozeScrimController.onScreenTurnedOn(); } @@ -4107,7 +4111,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mScrimController.dontAnimateBouncerChangesUntilNextFrame(); mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L); } - if (mStatusBarKeyguardViewManager.isScreenTurnedOn()) { + if (mScreenTurningOn || mStatusBarKeyguardViewManager.isScreenTurnedOn()) { mNotificationPanel.launchCamera(mDeviceInteractive /* animate */); } else { // We need to defer the camera launch until the screen comes on, since otherwise diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SecureCameraLaunchManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SecureCameraLaunchManager.java deleted file mode 100644 index 45c8938..0000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SecureCameraLaunchManager.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.phone; - -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.hardware.camera2.CameraManager; -import android.os.AsyncTask; -import android.os.Handler; -import android.provider.MediaStore; -import android.util.Log; - -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardUpdateMonitor; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Handles launching the secure camera properly even when other applications may be using the camera - * hardware. - * - * When other applications (e.g., Face Unlock) are using the camera, they must close the camera to - * allow the secure camera to open it. Since we want to minimize the delay when opening the secure - * camera, other apps should close the camera at the first possible opportunity (i.e., as soon as - * the user begins swiping to go to the secure camera). - * - * If the camera is unavailable when the user begins to swipe, the SecureCameraLaunchManager sends a - * broadcast to tell other apps to close the camera. When and if the user completes their swipe to - * launch the secure camera, the SecureCameraLaunchManager delays launching the secure camera until - * a callback indicates that the camera has become available. If it doesn't receive that callback - * within a specified timeout period, the secure camera is launched anyway. - * - * Ideally, the secure camera would handle waiting for the camera to become available. This allows - * some of the time necessary to close the camera to happen in parallel with starting the secure - * camera app. We can't rely on all third-party camera apps to handle this. However, an app can - * put com.android.systemui.statusbar.phone.will_wait_for_camera_available in its meta-data to - * indicate that it will be responsible for waiting for the camera to become available. - * - * It is assumed that the functions in this class, including the constructor, will be called from - * the UI thread. - */ -public class SecureCameraLaunchManager { - private static final boolean DEBUG = false; - private static final String TAG = "SecureCameraLaunchManager"; - - // Action sent as a broadcast to tell other apps to stop using the camera. Other apps that use - // the camera from keyguard (e.g., Face Unlock) should listen for this broadcast and close the - // camera as soon as possible after receiving it. - private static final String CLOSE_CAMERA_ACTION_NAME = - "com.android.systemui.statusbar.phone.CLOSE_CAMERA"; - - // Apps should put this field in their meta-data to indicate that they will take on the - // responsibility of waiting for the camera to become available. If this field is present, the - // SecureCameraLaunchManager launches the secure camera even if the camera hardware has not - // become available. Having the secure camera app do the waiting is the optimal approach, but - // without this field, the SecureCameraLaunchManager doesn't launch the secure camera until the - // camera hardware is available. - private static final String META_DATA_WILL_WAIT_FOR_CAMERA_AVAILABLE = - "com.android.systemui.statusbar.phone.will_wait_for_camera_available"; - - // If the camera hardware hasn't become available after this period of time, the - // SecureCameraLaunchManager launches the secure camera anyway. - private static final int CAMERA_AVAILABILITY_TIMEOUT_MS = 1000; - - private Context mContext; - private Handler mHandler; - private LockPatternUtils mLockPatternUtils; - private KeyguardBottomAreaView mKeyguardBottomArea; - - private CameraManager mCameraManager; - private CameraAvailabilityCallback mCameraAvailabilityCallback; - private Map<String, Boolean> mCameraAvailabilityMap; - private boolean mWaitingToLaunchSecureCamera; - private Runnable mLaunchCameraRunnable; - - private class CameraAvailabilityCallback extends CameraManager.AvailabilityCallback { - @Override - public void onCameraUnavailable(String cameraId) { - if (DEBUG) Log.d(TAG, "onCameraUnavailble(" + cameraId + ")"); - mCameraAvailabilityMap.put(cameraId, false); - } - - @Override - public void onCameraAvailable(String cameraId) { - if (DEBUG) Log.d(TAG, "onCameraAvailable(" + cameraId + ")"); - mCameraAvailabilityMap.put(cameraId, true); - - // If we were waiting for the camera hardware to become available to launch the - // secure camera, we can launch it now if all cameras are available. If one or more - // cameras are still not available, we will get this callback again for those - // cameras. - if (mWaitingToLaunchSecureCamera && areAllCamerasAvailable()) { - mKeyguardBottomArea.launchCamera(); - mWaitingToLaunchSecureCamera = false; - - // We no longer need to launch the camera after the timeout hits. - mHandler.removeCallbacks(mLaunchCameraRunnable); - } - } - } - - public SecureCameraLaunchManager(Context context, KeyguardBottomAreaView keyguardBottomArea) { - mContext = context; - mHandler = new Handler(); - mLockPatternUtils = new LockPatternUtils(context); - mKeyguardBottomArea = keyguardBottomArea; - - mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); - mCameraAvailabilityCallback = new CameraAvailabilityCallback(); - - // An onCameraAvailable() or onCameraUnavailable() callback will be received for each camera - // when the availability callback is registered, thus initializing the map. - // - // Keeping track of the state of all cameras using the onCameraAvailable() and - // onCameraUnavailable() callbacks can get messy when dealing with hot-pluggable cameras. - // However, we have a timeout in place such that we will never hang waiting for cameras. - mCameraAvailabilityMap = new HashMap<String, Boolean>(); - - mWaitingToLaunchSecureCamera = false; - mLaunchCameraRunnable = new Runnable() { - @Override - public void run() { - if (mWaitingToLaunchSecureCamera) { - Log.w(TAG, "Timeout waiting for camera availability"); - mKeyguardBottomArea.launchCamera(); - mWaitingToLaunchSecureCamera = false; - } - } - }; - } - - /** - * Initializes the SecureCameraManager and starts listening for camera availability. - */ - public void create() { - mCameraManager.registerAvailabilityCallback(mCameraAvailabilityCallback, mHandler); - } - - /** - * Stops listening for camera availability and cleans up the SecureCameraManager. - */ - public void destroy() { - mCameraManager.unregisterAvailabilityCallback(mCameraAvailabilityCallback); - } - - /** - * Called when the user is starting to swipe horizontally, possibly to start the secure camera. - * Although this swipe ultimately may not result in the secure camera opening, we need to stop - * all other camera usage (e.g., Face Unlock) as soon as possible. We send out a broadcast to - * notify other apps that they should close the camera immediately. The broadcast is sent even - * if the camera appears to be available, because there could be an app that is about to open - * the camera. - */ - public void onSwipingStarted() { - if (DEBUG) Log.d(TAG, "onSwipingStarted"); - AsyncTask.execute(new Runnable() { - @Override - public void run() { - Intent intent = new Intent(); - intent.setAction(CLOSE_CAMERA_ACTION_NAME); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcast(intent); - } - }); - } - - /** - * Called when the secure camera should be started. If the camera is available or the secure - * camera app has indicated that it will wait for camera availability, the secure camera app is - * launched immediately. Otherwise, we wait for the camera to become available (or timeout) - * before launching the secure camera. - */ - public void startSecureCameraLaunch() { - if (DEBUG) Log.d(TAG, "startSecureCameraLunch"); - if (areAllCamerasAvailable() || targetWillWaitForCameraAvailable()) { - mKeyguardBottomArea.launchCamera(); - } else { - mWaitingToLaunchSecureCamera = true; - mHandler.postDelayed(mLaunchCameraRunnable, CAMERA_AVAILABILITY_TIMEOUT_MS); - } - } - - /** - * Returns true if all of the cameras we are tracking are currently available. - */ - private boolean areAllCamerasAvailable() { - for (boolean cameraAvailable: mCameraAvailabilityMap.values()) { - if (!cameraAvailable) { - return false; - } - } - return true; - } - - /** - * Determines if the secure camera app will wait for the camera hardware to become available - * before trying to open the camera. If so, we can fire off an intent to start the secure - * camera app before the camera is available. Otherwise, it is our responsibility to wait for - * the camera hardware to become available before firing off the intent to start the secure - * camera. - * - * Ideally we are able to fire off the secure camera intent as early as possibly so that, if the - * camera is closing, it can continue to close while the secure camera app is opening. This - * improves secure camera startup time. - */ - private boolean targetWillWaitForCameraAvailable() { - // Create intent that would launch the secure camera. - Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE) - .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); - PackageManager packageManager = mContext.getPackageManager(); - - // Get the list of applications that can handle the intent. - final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser( - intent, PackageManager.MATCH_DEFAULT_ONLY, KeyguardUpdateMonitor.getCurrentUser()); - if (appList.size() == 0) { - if (DEBUG) Log.d(TAG, "No targets found for secure camera intent"); - return false; - } - - // Get the application that the intent resolves to. - ResolveInfo resolved = packageManager.resolveActivityAsUser(intent, - PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA, - KeyguardUpdateMonitor.getCurrentUser()); - - if (resolved == null || resolved.activityInfo == null) { - return false; - } - - // If we would need to launch the resolver activity, then we can't assume that the target - // is one that would wait for the camera. - if (wouldLaunchResolverActivity(resolved, appList)) { - if (DEBUG) Log.d(TAG, "Secure camera intent would launch resolver"); - return false; - } - - // If the target doesn't have meta-data we must assume it won't wait for the camera. - if (resolved.activityInfo.metaData == null || resolved.activityInfo.metaData.isEmpty()) { - if (DEBUG) Log.d(TAG, "No meta-data found for secure camera application"); - return false; - } - - // Check the secure camera app meta-data to see if it indicates that it will wait for the - // camera to become available. - boolean willWaitForCameraAvailability = - resolved.activityInfo.metaData.getBoolean(META_DATA_WILL_WAIT_FOR_CAMERA_AVAILABLE); - - if (DEBUG) Log.d(TAG, "Target will wait for camera: " + willWaitForCameraAvailability); - - return willWaitForCameraAvailability; - } - - /** - * Determines if the activity that would be launched by the intent is the ResolverActivity. - */ - private boolean wouldLaunchResolverActivity(ResolveInfo resolved, List<ResolveInfo> appList) { - // If the list contains the resolved activity, then it can't be the ResolverActivity itself. - for (int i = 0; i < appList.size(); i++) { - ResolveInfo tmp = appList.get(i); - if (tmp.activityInfo.name.equals(resolved.activityInfo.name) - && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) { - return false; - } - } - return true; - } -} diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 1f4427f..d1e1683 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -359,6 +359,9 @@ public class ConnectivityService extends IConnectivityManager.Stub */ private static final int EVENT_REGISTER_NETWORK_LISTENER_WITH_INTENT = 31; + /** Handler thread used for both of the handlers below. */ + @VisibleForTesting + protected final HandlerThread mHandlerThread; /** Handler used for internal events. */ final private InternalHandler mHandler; /** Handler used for incoming {@link NetworkStateTracker} events. */ @@ -614,6 +617,11 @@ public class ConnectivityService extends IConnectivityManager.Stub } private LegacyTypeTracker mLegacyTypeTracker = new LegacyTypeTracker(); + @VisibleForTesting + protected HandlerThread createHandlerThread() { + return new HandlerThread("ConnectivityServiceThread"); + } + public ConnectivityService(Context context, INetworkManagementService netManager, INetworkStatsService statsService, INetworkPolicyManager policyManager) { if (DBG) log("ConnectivityService starting up"); @@ -627,10 +635,10 @@ public class ConnectivityService extends IConnectivityManager.Stub mDefaultMobileDataRequest = createInternetRequestForTransport( NetworkCapabilities.TRANSPORT_CELLULAR); - HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread"); - handlerThread.start(); - mHandler = new InternalHandler(handlerThread.getLooper()); - mTrackerHandler = new NetworkStateTrackerHandler(handlerThread.getLooper()); + mHandlerThread = createHandlerThread(); + mHandlerThread.start(); + mHandler = new InternalHandler(mHandlerThread.getLooper()); + mTrackerHandler = new NetworkStateTrackerHandler(mHandlerThread.getLooper()); // setup our unique device name if (TextUtils.isEmpty(SystemProperties.get("net.hostname"))) { @@ -1458,7 +1466,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } private void enforceKeepalivePermission() { - mContext.enforceCallingPermission(KeepaliveTracker.PERMISSION, "ConnectivityService"); + mContext.enforceCallingOrSelfPermission(KeepaliveTracker.PERMISSION, "ConnectivityService"); } public void sendConnectedBroadcast(NetworkInfo info) { diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java index 342a3ef..69f0cef 100644 --- a/services/core/java/com/android/server/GestureLauncherService.java +++ b/services/core/java/com/android/server/GestureLauncherService.java @@ -17,7 +17,6 @@ package com.android.server; import android.app.ActivityManager; -import android.app.KeyguardManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -33,10 +32,11 @@ import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.SystemClock; import android.os.SystemProperties; -import android.os.Vibrator; import android.provider.Settings; import android.util.Slog; +import android.view.KeyEvent; +import com.android.internal.logging.MetricsLogger; import com.android.server.statusbar.StatusBarManagerInternal; /** @@ -46,10 +46,16 @@ import com.android.server.statusbar.StatusBarManagerInternal; * added.</p> * @hide */ -class GestureLauncherService extends SystemService { +public class GestureLauncherService extends SystemService { private static final boolean DBG = false; private static final String TAG = "GestureLauncherService"; + /** + * Time in milliseconds in which the power button must be pressed twice so it will be considered + * as a camera launch. + */ + private static final long CAMERA_POWER_DOUBLE_TAP_TIME_MS = 300; + /** The listener that receives the gesture event. */ private final GestureEventListener mGestureListener = new GestureEventListener(); @@ -91,13 +97,20 @@ class GestureLauncherService extends SystemService { */ private int mCameraLaunchLastEventExtra = 0; + /** + * Whether camera double tap power button gesture is currently enabled; + */ + private boolean mCameraDoubleTapPowerEnabled; + private long mLastPowerDownWhileNonInteractive = 0; + + public GestureLauncherService(Context context) { super(context); mContext = context; } public void onStart() { - // Nothing to publish. + LocalServices.addService(GestureLauncherService.class, this); } public void onBootPhase(int phase) { @@ -113,17 +126,21 @@ class GestureLauncherService extends SystemService { mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "GestureLauncherService"); updateCameraRegistered(); + updateCameraDoubleTapPowerEnabled(); mUserId = ActivityManager.getCurrentUser(); mContext.registerReceiver(mUserReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED)); - registerContentObserver(); + registerContentObservers(); } } - private void registerContentObserver() { + private void registerContentObservers() { mContext.getContentResolver().registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.CAMERA_GESTURE_DISABLED), false, mSettingObserver, mUserId); + mContext.getContentResolver().registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED), + false, mSettingObserver, mUserId); } private void updateCameraRegistered() { @@ -135,6 +152,13 @@ class GestureLauncherService extends SystemService { } } + private void updateCameraDoubleTapPowerEnabled() { + boolean enabled = isCameraDoubleTapPowerSettingEnabled(mContext, mUserId); + synchronized (this) { + mCameraDoubleTapPowerEnabled = enabled; + } + } + private void unregisterCameraLaunchGesture() { if (mRegistered) { mRegistered = false; @@ -197,6 +221,12 @@ class GestureLauncherService extends SystemService { Settings.Secure.CAMERA_GESTURE_DISABLED, 0, userId) == 0); } + public static boolean isCameraDoubleTapPowerSettingEnabled(Context context, int userId) { + return isCameraDoubleTapPowerEnabled(context.getResources()) + && (Settings.Secure.getIntForUser(context.getContentResolver(), + Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, 0, userId) == 0); + } + /** * Whether to enable the camera launch gesture. */ @@ -207,13 +237,64 @@ class GestureLauncherService extends SystemService { !SystemProperties.getBoolean("gesture.disable_camera_launch", false); } + public static boolean isCameraDoubleTapPowerEnabled(Resources resources) { + return resources.getBoolean( + com.android.internal.R.bool.config_cameraDoubleTapPowerGestureEnabled); + } + /** * Whether GestureLauncherService should be enabled according to system properties. */ public static boolean isGestureLauncherEnabled(Resources resources) { - // For now, the only supported gesture is camera launch gesture, so whether to enable this - // service equals to isCameraLaunchEnabled(); - return isCameraLaunchEnabled(resources); + return isCameraLaunchEnabled(resources) || isCameraDoubleTapPowerEnabled(resources); + } + + public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive) { + boolean launched = false; + synchronized (this) { + if (!mCameraDoubleTapPowerEnabled) { + mLastPowerDownWhileNonInteractive = 0; + return false; + } + if (event.getEventTime() - mLastPowerDownWhileNonInteractive + < CAMERA_POWER_DOUBLE_TAP_TIME_MS) { + launched = true; + } + mLastPowerDownWhileNonInteractive = interactive ? 0 : event.getEventTime(); + } + if (launched) { + Slog.i(TAG, "Power button double tap gesture detected, launching camera."); + launched = handleCameraLaunchGesture(false /* useWakelock */, + MetricsLogger.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE); + } + return launched; + } + + /** + * @return true if camera was launched, false otherwise. + */ + private boolean handleCameraLaunchGesture(boolean useWakelock, int logCategory) { + boolean userSetupComplete = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.USER_SETUP_COMPLETE, 0) != 0; + if (!userSetupComplete) { + if (DBG) Slog.d(TAG, String.format( + "userSetupComplete = %s, ignoring camera launch gesture.", + userSetupComplete)); + return false; + } + if (DBG) Slog.d(TAG, String.format( + "userSetupComplete = %s, performing camera launch gesture.", + userSetupComplete)); + + if (useWakelock) { + // Make sure we don't sleep too early + mWakeLock.acquire(500L); + } + StatusBarManagerInternal service = LocalServices.getService( + StatusBarManagerInternal.class); + service.onCameraLaunchGestureDetected(); + MetricsLogger.action(mContext, logCategory); + return true; } private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() { @@ -222,8 +303,9 @@ class GestureLauncherService extends SystemService { if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) { mUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); mContext.getContentResolver().unregisterContentObserver(mSettingObserver); - registerContentObserver(); + registerContentObservers(); updateCameraRegistered(); + updateCameraDoubleTapPowerEnabled(); } } }; @@ -232,6 +314,7 @@ class GestureLauncherService extends SystemService { public void onChange(boolean selfChange, android.net.Uri uri, int userId) { if (userId == mUserId) { updateCameraRegistered(); + updateCameraDoubleTapPowerEnabled(); } } }; @@ -244,36 +327,17 @@ class GestureLauncherService extends SystemService { return; } if (event.sensor == mCameraLaunchSensor) { - handleCameraLaunchGesture(event); - return; - } - } - - private void handleCameraLaunchGesture(SensorEvent event) { - if (DBG) { - float[] values = event.values; - Slog.d(TAG, String.format("Received a camera launch event: " + - "values=[%.4f, %.4f, %.4f].", values[0], values[1], values[2])); - } - boolean userSetupComplete = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.USER_SETUP_COMPLETE, 0) != 0; - if (!userSetupComplete) { - if (DBG) Slog.d(TAG, String.format( - "userSetupComplete = %s, ignoring camera launch gesture.", - userSetupComplete)); + if (DBG) { + float[] values = event.values; + Slog.d(TAG, String.format("Received a camera launch event: " + + "values=[%.4f, %.4f, %.4f].", values[0], values[1], values[2])); + } + if (handleCameraLaunchGesture(true /* useWakelock */, + MetricsLogger.ACTION_WIGGLE_CAMERA_GESTURE)) { + trackCameraLaunchEvent(event); + } return; } - if (DBG) Slog.d(TAG, String.format( - "userSetupComplete = %s, performing camera launch gesture.", - userSetupComplete)); - - // Make sure we don't sleep too early - mWakeLock.acquire(500L); - StatusBarManagerInternal service = LocalServices.getService( - StatusBarManagerInternal.class); - service.onCameraLaunchGestureDetected(); - trackCameraLaunchEvent(event); - mWakeLock.release(); } @Override diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index 0d64540..4e721da 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -368,11 +368,17 @@ class MountService extends IMountService.Stub private boolean shouldBenchmark() { final long benchInterval = Settings.Global.getLong(mContext.getContentResolver(), Settings.Global.STORAGE_BENCHMARK_INTERVAL, DateUtils.WEEK_IN_MILLIS); + if (benchInterval == -1) { + return false; + } else if (benchInterval == 0) { + return true; + } + synchronized (mLock) { for (int i = 0; i < mVolumes.size(); i++) { final VolumeInfo vol = mVolumes.valueAt(i); final VolumeRecord rec = mRecords.get(vol.fsUuid); - if (vol.isMountedReadable() && rec != null) { + if (vol.isMountedWritable() && rec != null) { final long benchAge = System.currentTimeMillis() - rec.lastBenchMillis; if (benchAge >= benchInterval) { return true; diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 8b0e6f2..d5e9a32 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -32,6 +32,7 @@ import android.accounts.IAccountManagerResponse; import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.AppGlobals; +import android.app.AppOpsManager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -122,6 +123,7 @@ public class AccountManagerService private final Context mContext; private final PackageManager mPackageManager; + private final AppOpsManager mAppOpsManager; private UserManager mUserManager; private final MessageHandler mMessageHandler; @@ -266,6 +268,7 @@ public class AccountManagerService IAccountAuthenticatorCache authenticatorCache) { mContext = context; mPackageManager = packageManager; + mAppOpsManager = mContext.getSystemService(AppOpsManager.class); mMessageHandler = new MessageHandler(FgThread.get().getLooper()); @@ -510,7 +513,7 @@ public class AccountManagerService // Check if there's a shared account that needs to be created as an account Account[] sharedAccounts = getSharedAccountsAsUser(userId); if (sharedAccounts == null || sharedAccounts.length == 0) return; - Account[] accounts = getAccountsAsUser(null, userId); + Account[] accounts = getAccountsAsUser(null, userId, mContext.getOpPackageName()); for (Account sa : sharedAccounts) { if (ArrayUtils.contains(accounts, sa)) continue; // Account doesn't exist. Copy it now. @@ -868,7 +871,8 @@ public class AccountManagerService // Confirm that the owner's account still exists before this step. UserAccounts owner = getUserAccounts(UserHandle.USER_OWNER); synchronized (owner.cacheLock) { - for (Account acc : getAccounts(UserHandle.USER_OWNER)) { + for (Account acc : getAccounts(UserHandle.USER_OWNER, + mContext.getOpPackageName())) { if (acc.equals(account)) { mAuthenticator.addAccountFromCredentials( this, account, accountCredentials); @@ -988,7 +992,7 @@ public class AccountManagerService @Override public void hasFeatures(IAccountManagerResponse response, - Account account, String[] features) { + Account account, String[] features, String opPackageName) { int callingUid = Binder.getCallingUid(); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "hasFeatures: " + account @@ -1001,7 +1005,8 @@ public class AccountManagerService if (account == null) throw new IllegalArgumentException("account is null"); if (features == null) throw new IllegalArgumentException("features is null"); int userId = UserHandle.getCallingUserId(); - checkReadAccountsPermitted(callingUid, account.type, userId); + checkReadAccountsPermitted(callingUid, account.type, userId, + opPackageName); long identityToken = clearCallingIdentity(); try { @@ -2507,9 +2512,10 @@ public class AccountManagerService * Returns the accounts visible to the client within the context of a specific user * @hide */ - public Account[] getAccounts(int userId) { + public Account[] getAccounts(int userId, String opPackageName) { int callingUid = Binder.getCallingUid(); - List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId); + List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId, + opPackageName); if (visibleAccountTypes.isEmpty()) { return new Account[0]; } @@ -2571,15 +2577,16 @@ public class AccountManagerService } @Override - public Account[] getAccountsAsUser(String type, int userId) { - return getAccountsAsUser(type, userId, null, -1); + public Account[] getAccountsAsUser(String type, int userId, String opPackageName) { + return getAccountsAsUser(type, userId, null, -1, opPackageName); } private Account[] getAccountsAsUser( String type, int userId, String callingPackage, - int packageUid) { + int packageUid, + String opPackageName) { int callingUid = Binder.getCallingUid(); // Only allow the system process to read accounts of other users if (userId != UserHandle.getCallingUserId() @@ -2600,9 +2607,11 @@ public class AccountManagerService // be passed in the original caller's uid here, which is what should be used for filtering. if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) { callingUid = packageUid; + opPackageName = callingPackage; } - List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId); + List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId, + opPackageName); if (visibleAccountTypes.isEmpty() || (type != null && !visibleAccountTypes.contains(type))) { return new Account[0]; @@ -2741,22 +2750,24 @@ public class AccountManagerService } @Override - public Account[] getAccounts(String type) { - return getAccountsAsUser(type, UserHandle.getCallingUserId()); + public Account[] getAccounts(String type, String opPackageName) { + return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName); } @Override - public Account[] getAccountsForPackage(String packageName, int uid) { + public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) { int callingUid = Binder.getCallingUid(); if (!UserHandle.isSameApp(callingUid, Process.myUid())) { throw new SecurityException("getAccountsForPackage() called from unauthorized uid " + callingUid + " with uid=" + uid); } - return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid); + return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid, + opPackageName); } @Override - public Account[] getAccountsByTypeForPackage(String type, String packageName) { + public Account[] getAccountsByTypeForPackage(String type, String packageName, + String opPackageName) { int packageUid = -1; try { packageUid = AppGlobals.getPackageManager().getPackageUid( @@ -2765,14 +2776,16 @@ public class AccountManagerService Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re); return new Account[0]; } - return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName, packageUid); + return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName, + packageUid, opPackageName); } @Override public void getAccountsByFeatures( IAccountManagerResponse response, String type, - String[] features) { + String[] features, + String opPackageName) { int callingUid = Binder.getCallingUid(); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "getAccounts: accountType " + type @@ -2785,7 +2798,8 @@ public class AccountManagerService if (type == null) throw new IllegalArgumentException("accountType is null"); int userId = UserHandle.getCallingUserId(); - List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId); + List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId, + opPackageName); if (!visibleAccountTypes.contains(type)) { Bundle result = new Bundle(); // Need to return just the accounts that are from matching signatures. @@ -3685,31 +3699,22 @@ public class AccountManagerService } } - private boolean isPermitted(int callingUid, String... permissions) { + private boolean isPermitted(String opPackageName, int callingUid, String... permissions) { for (String perm : permissions) { if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, " caller uid " + callingUid + " has " + perm); } - return true; + final int opCode = AppOpsManager.permissionToOpCode(perm); + if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp( + opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) { + return true; + } } } return false; } - /** Succeeds if any of the specified permissions are granted. */ - private void checkBinderPermission(String... permissions) { - final int callingUid = Binder.getCallingUid(); - if (isPermitted(callingUid, permissions)) { - String msg = String.format( - "caller uid %s lacks any of %s", - callingUid, - TextUtils.join(",", permissions)); - Log.w(TAG, " " + msg); - throw new SecurityException(msg); - } - } - private int handleIncomingUser(int userId) { try { return ActivityManagerNative.getDefault().handleIncomingUser( @@ -3763,11 +3768,13 @@ public class AccountManagerService return fromAuthenticator || hasExplicitGrants || isPrivileged; } - private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId) { + private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId, + String opPackageName) { if (accountType == null) { return false; } else { - return getTypesVisibleToCaller(callingUid, userId).contains(accountType); + return getTypesVisibleToCaller(callingUid, userId, + opPackageName).contains(accountType); } } @@ -3779,9 +3786,10 @@ public class AccountManagerService } } - private List<String> getTypesVisibleToCaller(int callingUid, int userId) { + private List<String> getTypesVisibleToCaller(int callingUid, int userId, + String opPackageName) { boolean isPermitted = - isPermitted(callingUid, Manifest.permission.GET_ACCOUNTS, + isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS, Manifest.permission.GET_ACCOUNTS_PRIVILEGED); Log.i(TAG, String.format("getTypesVisibleToCaller: isPermitted? %s", isPermitted)); return getTypesForCaller(callingUid, userId, isPermitted); @@ -3877,8 +3885,9 @@ public class AccountManagerService private void checkReadAccountsPermitted( int callingUid, String accountType, - int userId) { - if (!isAccountVisibleToCaller(accountType, callingUid, userId)) { + int userId, + String opPackageName) { + if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) { String msg = String.format( "caller uid %s cannot access %s accounts", callingUid, diff --git a/services/core/java/com/android/server/connectivity/KeepalivePacketData.java b/services/core/java/com/android/server/connectivity/KeepalivePacketData.java index 64b9399..2ccfdd1 100644 --- a/services/core/java/com/android/server/connectivity/KeepalivePacketData.java +++ b/services/core/java/com/android/server/connectivity/KeepalivePacketData.java @@ -71,6 +71,7 @@ public class KeepalivePacketData { // Check we have two IP addresses of the same family. if (srcAddress == null || dstAddress == null || !srcAddress.getClass().getName().equals(dstAddress.getClass().getName())) { + throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS); } // Set the protocol. @@ -102,7 +103,7 @@ public class KeepalivePacketData { InetAddress srcAddress, int srcPort, InetAddress dstAddress, int dstPort) throws InvalidPacketException { - if (!(srcAddress instanceof Inet4Address)) { + if (!(srcAddress instanceof Inet4Address) || !(dstAddress instanceof Inet4Address)) { throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS); } diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java index c78f347..90c9ddf 100644 --- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java +++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java @@ -62,8 +62,7 @@ public class KeepaliveTracker { private static final String TAG = "KeepaliveTracker"; private static final boolean DBG = true; - // TODO: Change this to a system-only permission. - public static final String PERMISSION = android.Manifest.permission.CHANGE_NETWORK_STATE; + public static final String PERMISSION = android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD; /** Keeps track of keepalive requests. */ private final HashMap <NetworkAgentInfo, HashMap<Integer, KeepaliveInfo>> mKeepalives = @@ -208,6 +207,8 @@ public class KeepaliveTracker { Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.name()); mNai.asyncChannel.sendMessage(CMD_STOP_PACKET_KEEPALIVE, mSlot); } + // TODO: at the moment we unconditionally return failure here. In cases where the + // NetworkAgent is alive, should we ask it to reply, so it can return failure? notifyMessenger(mSlot, reason); unlinkDeathRecipient(); } @@ -233,17 +234,14 @@ public class KeepaliveTracker { mKeepalives.put(nai, networkKeepalives); } - // Find the lowest-numbered free slot. + // Find the lowest-numbered free slot. Slot numbers start from 1, because that's what two + // separate chipset implementations independently came up with. int slot; - for (slot = 0; slot < networkKeepalives.size(); slot++) { + for (slot = 1; slot <= networkKeepalives.size(); slot++) { if (networkKeepalives.get(slot) == null) { return slot; } } - // No free slot, pick one at the end. - - // HACK for broadcom hardware that does not support slot 0! - if (slot == 0) slot = 1; return slot; } @@ -267,14 +265,15 @@ public class KeepaliveTracker { } public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) { + String networkName = (nai == null) ? "(null)" : nai.name(); HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai); if (networkKeepalives == null) { - Log.e(TAG, "Attempt to stop keepalive on nonexistent network " + nai.name()); + Log.e(TAG, "Attempt to stop keepalive on nonexistent network " + networkName); return; } KeepaliveInfo ki = networkKeepalives.get(slot); if (ki == null) { - Log.e(TAG, "Attempt to stop nonexistent keepalive " + slot + " on " + nai.name()); + Log.e(TAG, "Attempt to stop nonexistent keepalive " + slot + " on " + networkName); return; } ki.stop(reason); @@ -332,6 +331,11 @@ public class KeepaliveTracker { public void startNattKeepalive(NetworkAgentInfo nai, int intervalSeconds, Messenger messenger, IBinder binder, String srcAddrString, int srcPort, String dstAddrString, int dstPort) { + if (nai == null) { + notifyMessenger(messenger, NO_KEEPALIVE, ERROR_INVALID_NETWORK); + return; + } + InetAddress srcAddress, dstAddress; try { srcAddress = NetworkUtils.numericToInetAddress(srcAddrString); diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index c998c2c..334bc18 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -340,7 +340,8 @@ public class SyncManager { for (UserInfo user : mUserManager.getUsers(true)) { // Skip any partially created/removed users if (user.partial) continue; - Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts(user.id); + Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts( + user.id, mContext.getOpPackageName()); mSyncStorageEngine.doDatabaseCleanup(accountsForUser, user.id); } } @@ -1232,7 +1233,8 @@ public class SyncManager { } // Schedule sync for any accounts under started user - final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId); + final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId, + mContext.getOpPackageName()); for (Account account : accounts) { scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null, 0 /* no delay */, 0 /* No flex */, diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java index f53ccc9..2eabd32 100644 --- a/services/core/java/com/android/server/display/DisplayPowerState.java +++ b/services/core/java/com/android/server/display/DisplayPowerState.java @@ -341,7 +341,8 @@ final class DisplayPowerState { private int mPendingBacklight = INITIAL_BACKLIGHT; private int mActualState = INITIAL_SCREEN_STATE; private int mActualBacklight = INITIAL_BACKLIGHT; - private boolean mChangeInProgress; + private boolean mStateChangeInProgress; + private boolean mBacklightChangeInProgress; public PhotonicModulator() { super("PhotonicModulator"); @@ -349,7 +350,9 @@ final class DisplayPowerState { public boolean setState(int state, int backlight) { synchronized (mLock) { - if (state != mPendingState || backlight != mPendingBacklight) { + boolean stateChanged = state != mPendingState; + boolean backlightChanged = backlight != mPendingBacklight; + if (stateChanged || backlightChanged) { if (DEBUG) { Slog.d(TAG, "Requesting new screen state: state=" + Display.stateToString(state) + ", backlight=" + backlight); @@ -358,12 +361,15 @@ final class DisplayPowerState { mPendingState = state; mPendingBacklight = backlight; - if (!mChangeInProgress) { - mChangeInProgress = true; + boolean changeInProgress = mStateChangeInProgress || mBacklightChangeInProgress; + mStateChangeInProgress = stateChanged; + mBacklightChangeInProgress = backlightChanged; + + if (!changeInProgress) { mLock.notifyAll(); } } - return !mChangeInProgress; + return !mStateChangeInProgress; } } @@ -375,7 +381,8 @@ final class DisplayPowerState { pw.println(" mPendingBacklight=" + mPendingBacklight); pw.println(" mActualState=" + Display.stateToString(mActualState)); pw.println(" mActualBacklight=" + mActualBacklight); - pw.println(" mChangeInProgress=" + mChangeInProgress); + pw.println(" mStateChangeInProgress=" + mStateChangeInProgress); + pw.println(" mBacklightChangeInProgress=" + mBacklightChangeInProgress); } } @@ -392,10 +399,15 @@ final class DisplayPowerState { stateChanged = (state != mActualState); backlight = mPendingBacklight; backlightChanged = (backlight != mActualBacklight); - if (!stateChanged && !backlightChanged) { - // All changed applied, notify outer class and wait for more. - mChangeInProgress = false; + if (!stateChanged) { + // State changed applied, notify outer class. postScreenUpdateThreadSafe(); + mStateChangeInProgress = false; + } + if (!backlightChanged) { + mBacklightChangeInProgress = false; + } + if (!stateChanged && !backlightChanged) { try { mLock.wait(); } catch (InterruptedException ex) { } diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java index 0023258..ea7d85e 100644 --- a/services/core/java/com/android/server/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java @@ -21,10 +21,15 @@ import android.app.ActivityManager; import android.app.ActivityManager.RunningAppProcessInfo; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManagerNative; +import android.app.AlarmManager; import android.app.AppOpsManager; import android.app.IUserSwitchObserver; +import android.app.PendingIntent; import android.content.ComponentName; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.hardware.fingerprint.IFingerprintServiceLockoutResetCallback; @@ -85,6 +90,8 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe private static final String FINGERPRINTD = "android.hardware.fingerprint.IFingerprintDaemon"; private static final int MSG_USER_SWITCHING = 10; private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute + private static final String ACTION_LOCKOUT_RESET = + "com.android.server.fingerprint.ACTION_LOCKOUT_RESET"; private ClientMonitor mAuthClient = null; private ClientMonitor mEnrollClient = null; @@ -118,9 +125,19 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe private long mHalDeviceId; private int mFailedAttempts; private IFingerprintDaemon mDaemon; - private PowerManager mPowerManager; + private final PowerManager mPowerManager; + private final AlarmManager mAlarmManager; - private final Runnable mLockoutReset = new Runnable() { + private final BroadcastReceiver mLockoutReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (ACTION_LOCKOUT_RESET.equals(intent.getAction())) { + resetFailedAttempts(); + } + } + }; + + private final Runnable mResetFailedAttemptsRunnable = new Runnable() { @Override public void run() { resetFailedAttempts(); @@ -133,7 +150,10 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe mKeyguardPackage = ComponentName.unflattenFromString(context.getResources().getString( com.android.internal.R.string.config_keyguardComponent)).getPackageName(); mAppOps = context.getSystemService(AppOpsManager.class); - mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + mPowerManager = mContext.getSystemService(PowerManager.class); + mAlarmManager = mContext.getSystemService(AlarmManager.class); + mContext.registerReceiver(mLockoutReceiver, new IntentFilter(ACTION_LOCKOUT_RESET), + RESET_FINGERPRINT_LOCKOUT, null /* handler */); } @Override @@ -262,14 +282,28 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe return mFailedAttempts >= MAX_FAILED_ATTEMPTS; } + private void scheduleLockoutReset() { + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS, getLockoutResetIntent()); + } + + private void cancelLockoutReset() { + mAlarmManager.cancel(getLockoutResetIntent()); + } + + private PendingIntent getLockoutResetIntent() { + return PendingIntent.getBroadcast(mContext, 0, + new Intent(ACTION_LOCKOUT_RESET), PendingIntent.FLAG_UPDATE_CURRENT); + } + private void resetFailedAttempts() { if (DEBUG && inLockoutMode()) { Slog.v(TAG, "Reset fingerprint lockout"); } mFailedAttempts = 0; - // If we're asked to reset failed attempts externally (i.e. from Keyguard), the runnable - // may still be in the queue; remove it. - mHandler.removeCallbacks(mLockoutReset); + // If we're asked to reset failed attempts externally (i.e. from Keyguard), the alarm might + // still be pending; remove it. + cancelLockoutReset(); notifyLockoutResetMonitors(); } @@ -277,8 +311,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe mFailedAttempts++; if (inLockoutMode()) { // Failing multiple times will continue to push out the lockout time. - mHandler.removeCallbacks(mLockoutReset); - mHandler.postDelayed(mLockoutReset, FAIL_LOCKOUT_TIMEOUT_MS); + scheduleLockoutReset(); if (clientMonitor != null && !clientMonitor.sendError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)) { Slog.w(TAG, "Cannot send lockout message to client"); @@ -683,7 +716,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe FingerprintUtils.vibrateFingerprintSuccess(getContext()); } result |= true; // we have a valid fingerprint - mHandler.post(mLockoutReset); + resetFailedAttempts(); } return result; } @@ -1016,7 +1049,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe public void resetTimeout(byte [] token) { checkPermission(RESET_FINGERPRINT_LOCKOUT); // TODO: confirm security token when we move timeout management into the HAL layer. - mHandler.post(mLockoutReset); + mHandler.post(mResetFailedAttemptsRunnable); } @Override diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 34737c1..d7e3c54 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -117,6 +117,7 @@ import com.android.internal.R; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.ScreenShapeHelper; import com.android.internal.widget.PointerLocationView; +import com.android.server.GestureLauncherService; import com.android.server.LocalServices; import com.android.server.policy.keyguard.KeyguardServiceDelegate; import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener; @@ -125,7 +126,6 @@ import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; -import java.util.Arrays; import java.util.HashSet; import java.util.List; @@ -931,10 +931,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + GestureLauncherService gestureService = LocalServices.getService( + GestureLauncherService.class); + boolean gesturedServiceIntercepted = false; + if (gestureService != null) { + gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive); + } + // If the power key has still not yet been handled, then detect short // press, long press, or multi press and decide what to do. mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered - || mScreenshotChordVolumeUpKeyTriggered; + || mScreenshotChordVolumeUpKeyTriggered || gesturedServiceIntercepted; if (!mPowerKeyHandled) { if (interactive) { // When interactive, we're already awake. diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index 919293a..8bb20a6 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -39,6 +39,7 @@ <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> + <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" /> <application> <uses-library android:name="android.test.runner" /> diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java index b4c76b7..97e16da 100644 --- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java @@ -20,35 +20,9 @@ import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.getNetworkTypeName; -import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; -import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; -import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; -import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS; -import static android.net.NetworkCapabilities.NET_CAPABILITY_FOTA; -import static android.net.NetworkCapabilities.NET_CAPABILITY_IA; -import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS; -import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; -import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; -import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS; -import static android.net.NetworkCapabilities.NET_CAPABILITY_SUPL; -import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P; -import static android.net.NetworkCapabilities.NET_CAPABILITY_XCAP; -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.NetworkCapabilities.TRANSPORT_WIFI; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isA; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; +import static android.net.NetworkCapabilities.*; + import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -58,8 +32,12 @@ import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; +import android.net.ConnectivityManager.PacketKeepalive; +import android.net.ConnectivityManager.PacketKeepaliveCallback; import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; +import android.net.IpPrefix; +import android.net.LinkAddress; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkAgent; @@ -74,8 +52,11 @@ import android.net.RouteInfo; import android.os.ConditionVariable; import android.os.Handler; import android.os.HandlerThread; -import android.os.Looper; import android.os.INetworkManagementService; +import android.os.Looper; +import android.os.Message; +import android.os.MessageQueue; +import android.os.MessageQueue.IdleHandler; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; @@ -84,10 +65,10 @@ import android.util.LogPrinter; import com.android.server.connectivity.NetworkAgentInfo; import com.android.server.connectivity.NetworkMonitor; -import org.mockito.ArgumentCaptor; - import java.net.InetAddress; -import java.util.concurrent.Future; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -99,24 +80,7 @@ import java.util.concurrent.atomic.AtomicBoolean; public class ConnectivityServiceTest extends AndroidTestCase { private static final String TAG = "ConnectivityServiceTest"; - private static final String MOBILE_IFACE = "rmnet3"; - private static final String WIFI_IFACE = "wlan6"; - - private static final RouteInfo MOBILE_ROUTE_V4 = RouteInfo.makeHostRoute(parse("10.0.0.33"), - MOBILE_IFACE); - private static final RouteInfo MOBILE_ROUTE_V6 = RouteInfo.makeHostRoute(parse("fd00::33"), - MOBILE_IFACE); - - private static final RouteInfo WIFI_ROUTE_V4 = RouteInfo.makeHostRoute(parse("192.168.0.66"), - parse("192.168.0.1"), - WIFI_IFACE); - private static final RouteInfo WIFI_ROUTE_V6 = RouteInfo.makeHostRoute(parse("fd00::66"), - parse("fd00::"), - WIFI_IFACE); - - private INetworkManagementService mNetManager; - private INetworkStatsService mStatsService; - private INetworkPolicyManager mPolicyService; + private static final int TIMEOUT_MS = 500; private BroadcastInterceptingContext mServiceContext; private WrappedConnectivityService mService; @@ -148,14 +112,93 @@ public class ConnectivityServiceTest extends AndroidTestCase { } } + /** + * A subclass of HandlerThread that allows callers to wait for it to become idle. waitForIdle + * will return immediately if the handler is already idle. + */ + private class IdleableHandlerThread extends HandlerThread { + private IdleHandler mIdleHandler; + + public IdleableHandlerThread(String name) { + super(name); + } + + public void waitForIdle(int timeoutMs) { + final ConditionVariable cv = new ConditionVariable(); + final MessageQueue queue = getLooper().getQueue(); + + synchronized (queue) { + if (queue.isIdle()) { + return; + } + + assertNull("BUG: only one idle handler allowed", mIdleHandler); + mIdleHandler = new IdleHandler() { + public boolean queueIdle() { + cv.open(); + mIdleHandler = null; + return false; // Remove the handler. + } + }; + queue.addIdleHandler(mIdleHandler); + } + + if (!cv.block(timeoutMs)) { + fail("HandlerThread " + getName() + + " did not become idle after " + timeoutMs + " ms"); + queue.removeIdleHandler(mIdleHandler); + } + } + } + + // Tests that IdleableHandlerThread works as expected. + public void testIdleableHandlerThread() { + final int attempts = 50; // Causes the test to take about 200ms on bullhead-eng. + + // Tests that waitForIdle returns immediately if the service is already idle. + for (int i = 0; i < attempts; i++) { + mService.waitForIdle(); + } + + // Bring up a network that we can use to send messages to ConnectivityService. + ConditionVariable cv = waitForConnectivityBroadcasts(1); + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + waitFor(cv); + Network n = mWiFiNetworkAgent.getNetwork(); + assertNotNull(n); + + // Tests that calling waitForIdle waits for messages to be processed. + for (int i = 0; i < attempts; i++) { + mWiFiNetworkAgent.setSignalStrength(i); + mService.waitForIdle(); + assertEquals(i, mCm.getNetworkCapabilities(n).getSignalStrength()); + } + + // Ensure that not calling waitForIdle causes a race condition. + for (int i = 0; i < attempts; i++) { + mWiFiNetworkAgent.setSignalStrength(i); + if (i != mCm.getNetworkCapabilities(n).getSignalStrength()) { + // We hit a race condition, as expected. Pass the test. + return; + } + } + + // No race? There is a bug in this test. + fail("expected race condition at least once in " + attempts + " attempts"); + } + private class MockNetworkAgent { private final WrappedNetworkMonitor mWrappedNetworkMonitor; private final NetworkInfo mNetworkInfo; private final NetworkCapabilities mNetworkCapabilities; - private final Thread mThread; + private final IdleableHandlerThread mHandlerThread; private final ConditionVariable mDisconnected = new ConditionVariable(); private int mScore; private NetworkAgent mNetworkAgent; + private int mStartKeepaliveError = PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED; + private int mStopKeepaliveError = PacketKeepalive.NO_KEEPALIVE; + private Integer mExpectedKeepaliveSlot = null; MockNetworkAgent(int transport) { final int type = transportToLegacyType(transport); @@ -173,26 +216,42 @@ public class ConnectivityServiceTest extends AndroidTestCase { default: throw new UnsupportedOperationException("unimplemented network type"); } - final ConditionVariable initComplete = new ConditionVariable(); - final ConditionVariable networkMonitorAvailable = mService.getNetworkMonitorCreatedCV(); - mThread = new Thread() { - public void run() { - Looper.prepare(); - mNetworkAgent = new NetworkAgent(Looper.myLooper(), mServiceContext, - "Mock" + typeName, mNetworkInfo, mNetworkCapabilities, - new LinkProperties(), mScore, new NetworkMisc()) { - public void unwanted() { mDisconnected.open(); } - }; - initComplete.open(); - Looper.loop(); + mHandlerThread = new IdleableHandlerThread("Mock-" + typeName); + mHandlerThread.start(); + mNetworkAgent = new NetworkAgent(mHandlerThread.getLooper(), mServiceContext, + "Mock-" + typeName, mNetworkInfo, mNetworkCapabilities, + new LinkProperties(), mScore, new NetworkMisc()) { + @Override + public void unwanted() { mDisconnected.open(); } + + @Override + public void startPacketKeepalive(Message msg) { + int slot = msg.arg1; + if (mExpectedKeepaliveSlot != null) { + assertEquals((int) mExpectedKeepaliveSlot, slot); + } + onPacketKeepaliveEvent(slot, mStartKeepaliveError); + } + + @Override + public void stopPacketKeepalive(Message msg) { + onPacketKeepaliveEvent(msg.arg1, mStopKeepaliveError); } }; - mThread.start(); - waitFor(initComplete); - waitFor(networkMonitorAvailable); + // Waits for the NetworkAgent to be registered, which includes the creation of the + // NetworkMonitor. + mService.waitForIdle(); mWrappedNetworkMonitor = mService.getLastCreatedWrappedNetworkMonitor(); } + public void waitForIdle(int timeoutMs) { + mHandlerThread.waitForIdle(timeoutMs); + } + + public void waitForIdle() { + waitForIdle(TIMEOUT_MS); + } + public void adjustScore(int change) { mScore += change; mNetworkAgent.sendNetworkScore(mScore); @@ -203,6 +262,11 @@ public class ConnectivityServiceTest extends AndroidTestCase { mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); } + public void setSignalStrength(int signalStrength) { + mNetworkCapabilities.setSignalStrength(signalStrength); + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } + public void connectWithoutInternet() { mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null); mNetworkAgent.sendNetworkInfo(mNetworkInfo); @@ -272,15 +336,46 @@ public class ConnectivityServiceTest extends AndroidTestCase { public WrappedNetworkMonitor getWrappedNetworkMonitor() { return mWrappedNetworkMonitor; } + + public void sendLinkProperties(LinkProperties lp) { + mNetworkAgent.sendLinkProperties(lp); + } + + public void setStartKeepaliveError(int error) { + mStartKeepaliveError = error; + } + + public void setStopKeepaliveError(int error) { + mStopKeepaliveError = error; + } + + public void setExpectedKeepaliveSlot(Integer slot) { + mExpectedKeepaliveSlot = slot; + } } + /** + * A NetworkFactory that allows tests to wait until any in-flight NetworkRequest add or remove + * operations have been processed. Before ConnectivityService can add or remove any requests, + * the factory must be told to expect those operations by calling expectAddRequests or + * expectRemoveRequests. + */ private static class MockNetworkFactory extends NetworkFactory { private final ConditionVariable mNetworkStartedCV = new ConditionVariable(); private final ConditionVariable mNetworkStoppedCV = new ConditionVariable(); - private final ConditionVariable mNetworkRequestedCV = new ConditionVariable(); - private final ConditionVariable mNetworkReleasedCV = new ConditionVariable(); private final AtomicBoolean mNetworkStarted = new AtomicBoolean(false); + // Used to expect that requests be removed or added on a separate thread, without sleeping. + // Callers can call either expectAddRequests() or expectRemoveRequests() exactly once, then + // cause some other thread to add or remove requests, then call waitForRequests(). We can + // either expect requests to be added or removed, but not both, because CountDownLatch can + // only count in one direction. + private CountDownLatch mExpectations; + + // Whether we are currently expecting requests to be added or removed. Valid only if + // mExpectations is non-null. + private boolean mExpectingAdditions; + public MockNetworkFactory(Looper looper, Context context, String logTag, NetworkCapabilities filter) { super(looper, context, logTag, filter); @@ -314,28 +409,75 @@ public class ConnectivityServiceTest extends AndroidTestCase { return mNetworkStoppedCV; } - protected void needNetworkFor(NetworkRequest networkRequest, int score) { - super.needNetworkFor(networkRequest, score); - mNetworkRequestedCV.open(); + @Override + protected void handleAddRequest(NetworkRequest request, int score) { + // If we're expecting anything, we must be expecting additions. + if (mExpectations != null && !mExpectingAdditions) { + fail("Can't add requests while expecting requests to be removed"); + } + + // Add the request. + super.handleAddRequest(request, score); + + // Reduce the number of request additions we're waiting for. + if (mExpectingAdditions) { + assertTrue("Added more requests than expected", mExpectations.getCount() > 0); + mExpectations.countDown(); + } + } + + @Override + protected void handleRemoveRequest(NetworkRequest request) { + // If we're expecting anything, we must be expecting removals. + if (mExpectations != null && mExpectingAdditions) { + fail("Can't remove requests while expecting requests to be added"); + } + + // Remove the request. + super.handleRemoveRequest(request); + + // Reduce the number of request removals we're waiting for. + if (!mExpectingAdditions) { + assertTrue("Removed more requests than expected", mExpectations.getCount() > 0); + mExpectations.countDown(); + } } - protected void releaseNetworkFor(NetworkRequest networkRequest) { - super.releaseNetworkFor(networkRequest); - mNetworkReleasedCV.open(); + private void assertNoExpectations() { + if (mExpectations != null) { + fail("Can't add expectation, " + mExpectations.getCount() + " already pending"); + } + } + + // Expects that count requests will be added. + public void expectAddRequests(final int count) { + assertNoExpectations(); + mExpectingAdditions = true; + mExpectations = new CountDownLatch(count); } - public ConditionVariable getNetworkRequestedCV() { - mNetworkRequestedCV.close(); - return mNetworkRequestedCV; + // Expects that count requests will be removed. + public void expectRemoveRequests(final int count) { + assertNoExpectations(); + mExpectingAdditions = false; + mExpectations = new CountDownLatch(count); } - public ConditionVariable getNetworkReleasedCV() { - mNetworkReleasedCV.close(); - return mNetworkReleasedCV; + // Waits for the expected request additions or removals to happen within a timeout. + public void waitForRequests() throws InterruptedException { + assertNotNull("Nothing to wait for", mExpectations); + mExpectations.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); + final long count = mExpectations.getCount(); + final String msg = count + " requests still not " + + (mExpectingAdditions ? "added" : "removed") + + " after " + TIMEOUT_MS + " ms"; + assertEquals(msg, 0, count); + mExpectations = null; } - public void waitForNetworkRequests(final int count) { - waitFor(new Criteria() { public boolean get() { return count == getRequestCount(); } }); + public void waitForNetworkRequests(final int count) throws InterruptedException { + waitForRequests(); + assertEquals(count, getMyRequestCount()); } } @@ -356,7 +498,6 @@ public class ConnectivityServiceTest extends AndroidTestCase { } private class WrappedConnectivityService extends ConnectivityService { - private final ConditionVariable mNetworkMonitorCreated = new ConditionVariable(); private WrappedNetworkMonitor mLastCreatedNetworkMonitor; public WrappedConnectivityService(Context context, INetworkManagementService netManager, @@ -365,6 +506,11 @@ public class ConnectivityServiceTest extends AndroidTestCase { } @Override + protected HandlerThread createHandlerThread() { + return new IdleableHandlerThread("WrappedConnectivityService"); + } + + @Override protected int getDefaultTcpRwnd() { // Prevent wrapped ConnectivityService from trying to write to SystemProperties. return 0; @@ -397,7 +543,6 @@ public class ConnectivityServiceTest extends AndroidTestCase { final WrappedNetworkMonitor monitor = new WrappedNetworkMonitor(context, handler, nai, defaultRequest); mLastCreatedNetworkMonitor = monitor; - mNetworkMonitorCreated.open(); return monitor; } @@ -405,10 +550,14 @@ public class ConnectivityServiceTest extends AndroidTestCase { return mLastCreatedNetworkMonitor; } - public ConditionVariable getNetworkMonitorCreatedCV() { - mNetworkMonitorCreated.close(); - return mNetworkMonitorCreated; + public void waitForIdle(int timeoutMs) { + ((IdleableHandlerThread) mHandlerThread).waitForIdle(timeoutMs); } + + public void waitForIdle() { + waitForIdle(TIMEOUT_MS); + } + } private interface Criteria { @@ -431,23 +580,11 @@ public class ConnectivityServiceTest extends AndroidTestCase { } /** - * Wait up to 500ms for {@code conditonVariable} to open. - * Fails if 500ms goes by before {@code conditionVariable} opens. + * Wait up to TIMEOUT_MS for {@code conditionVariable} to open. + * Fails if TIMEOUT_MS goes by before {@code conditionVariable} opens. */ static private void waitFor(ConditionVariable conditionVariable) { - assertTrue(conditionVariable.block(500)); - } - - /** - * This should only be used to verify that nothing happens, in other words that no unexpected - * changes occur. It should never be used to wait for a specific positive signal to occur. - */ - private void shortSleep() { - // TODO: Instead of sleeping, instead wait for all message loops to idle. - try { - Thread.sleep(500); - } catch (InterruptedException e) { - } + assertTrue(conditionVariable.block(TIMEOUT_MS)); } @Override @@ -455,13 +592,11 @@ public class ConnectivityServiceTest extends AndroidTestCase { super.setUp(); mServiceContext = new MockContext(getContext()); + mService = new WrappedConnectivityService(mServiceContext, + mock(INetworkManagementService.class), + mock(INetworkStatsService.class), + mock(INetworkPolicyManager.class)); - mNetManager = mock(INetworkManagementService.class); - mStatsService = mock(INetworkStatsService.class); - mPolicyService = mock(INetworkPolicyManager.class); - - mService = new WrappedConnectivityService( - mServiceContext, mNetManager, mStatsService, mPolicyService); mService.systemReady(); mCm = new ConnectivityManager(getContext(), mService); } @@ -583,11 +718,11 @@ public class ConnectivityServiceTest extends AndroidTestCase { // Test bringing up unvalidated cellular mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(false); - shortSleep(); + mService.waitForIdle(); verifyActiveNetwork(TRANSPORT_WIFI); // Test cellular disconnect. mCellNetworkAgent.disconnect(); - shortSleep(); + mService.waitForIdle(); verifyActiveNetwork(TRANSPORT_WIFI); // Test bringing up validated cellular mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); @@ -797,6 +932,11 @@ public class ConnectivityServiceTest extends AndroidTestCase { LOST } + /** + * Utility NetworkCallback for testing. The caller must explicitly test for all the callbacks + * this class receives, by calling expectCallback() exactly once each time a callback is + * received. assertNoCallback may be called at any time. + */ private class TestNetworkCallback extends NetworkCallback { private final ConditionVariable mConditionVariable = new ConditionVariable(); private CallbackState mLastCallback = CallbackState.NONE; @@ -819,14 +959,15 @@ public class ConnectivityServiceTest extends AndroidTestCase { mConditionVariable.open(); } - ConditionVariable getConditionVariable() { + void expectCallback(CallbackState state) { + waitFor(mConditionVariable); + assertEquals(state, mLastCallback); mLastCallback = CallbackState.NONE; mConditionVariable.close(); - return mConditionVariable; } - CallbackState getLastCallback() { - return mLastCallback; + void assertNoCallback() { + assertEquals(CallbackState.NONE, mLastCallback); } } @@ -842,98 +983,68 @@ public class ConnectivityServiceTest extends AndroidTestCase { mCm.registerNetworkCallback(cellRequest, cellNetworkCallback); // Test unvalidated networks - ConditionVariable cellCv = cellNetworkCallback.getConditionVariable(); - ConditionVariable wifiCv = wifiNetworkCallback.getConditionVariable(); ConditionVariable cv = waitForConnectivityBroadcasts(1); mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(false); - waitFor(cellCv); - assertEquals(CallbackState.AVAILABLE, cellNetworkCallback.getLastCallback()); - assertEquals(CallbackState.NONE, wifiNetworkCallback.getLastCallback()); + cellNetworkCallback.expectCallback(CallbackState.AVAILABLE); + wifiNetworkCallback.assertNoCallback(); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); waitFor(cv); - cellCv = cellNetworkCallback.getConditionVariable(); - wifiCv = wifiNetworkCallback.getConditionVariable(); // This should not trigger spurious onAvailable() callbacks, b/21762680. mCellNetworkAgent.adjustScore(-1); - shortSleep(); - assertEquals(CallbackState.NONE, wifiNetworkCallback.getLastCallback()); - assertEquals(CallbackState.NONE, cellNetworkCallback.getLastCallback()); + mService.waitForIdle(); + wifiNetworkCallback.assertNoCallback(); + cellNetworkCallback.assertNoCallback(); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - cellCv = cellNetworkCallback.getConditionVariable(); - wifiCv = wifiNetworkCallback.getConditionVariable(); cv = waitForConnectivityBroadcasts(2); mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); - waitFor(wifiCv); - assertEquals(CallbackState.AVAILABLE, wifiNetworkCallback.getLastCallback()); - assertEquals(CallbackState.NONE, cellNetworkCallback.getLastCallback()); + wifiNetworkCallback.expectCallback(CallbackState.AVAILABLE); + cellNetworkCallback.assertNoCallback(); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); waitFor(cv); - cellCv = cellNetworkCallback.getConditionVariable(); - wifiCv = wifiNetworkCallback.getConditionVariable(); cv = waitForConnectivityBroadcasts(2); mWiFiNetworkAgent.disconnect(); - waitFor(wifiCv); - assertEquals(CallbackState.LOST, wifiNetworkCallback.getLastCallback()); - assertEquals(CallbackState.NONE, cellNetworkCallback.getLastCallback()); + wifiNetworkCallback.expectCallback(CallbackState.LOST); + cellNetworkCallback.assertNoCallback(); waitFor(cv); - cellCv = cellNetworkCallback.getConditionVariable(); - wifiCv = wifiNetworkCallback.getConditionVariable(); cv = waitForConnectivityBroadcasts(1); mCellNetworkAgent.disconnect(); - waitFor(cellCv); - assertEquals(CallbackState.LOST, cellNetworkCallback.getLastCallback()); - assertEquals(CallbackState.NONE, wifiNetworkCallback.getLastCallback()); + cellNetworkCallback.expectCallback(CallbackState.LOST); + wifiNetworkCallback.assertNoCallback(); waitFor(cv); // Test validated networks - - cellCv = cellNetworkCallback.getConditionVariable(); - wifiCv = wifiNetworkCallback.getConditionVariable(); mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); - waitFor(cellCv); - assertEquals(CallbackState.AVAILABLE, cellNetworkCallback.getLastCallback()); - assertEquals(CallbackState.NONE, wifiNetworkCallback.getLastCallback()); + cellNetworkCallback.expectCallback(CallbackState.AVAILABLE); + wifiNetworkCallback.assertNoCallback(); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - cellCv = cellNetworkCallback.getConditionVariable(); - wifiCv = wifiNetworkCallback.getConditionVariable(); // This should not trigger spurious onAvailable() callbacks, b/21762680. mCellNetworkAgent.adjustScore(-1); - shortSleep(); - assertEquals(CallbackState.NONE, wifiNetworkCallback.getLastCallback()); - assertEquals(CallbackState.NONE, cellNetworkCallback.getLastCallback()); + mService.waitForIdle(); + wifiNetworkCallback.assertNoCallback(); + cellNetworkCallback.assertNoCallback(); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - cellCv = cellNetworkCallback.getConditionVariable(); - wifiCv = wifiNetworkCallback.getConditionVariable(); mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); - waitFor(wifiCv); - assertEquals(CallbackState.AVAILABLE, wifiNetworkCallback.getLastCallback()); - waitFor(cellCv); - assertEquals(CallbackState.LOSING, cellNetworkCallback.getLastCallback()); + wifiNetworkCallback.expectCallback(CallbackState.AVAILABLE); + cellNetworkCallback.expectCallback(CallbackState.LOSING); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - cellCv = cellNetworkCallback.getConditionVariable(); - wifiCv = wifiNetworkCallback.getConditionVariable(); mWiFiNetworkAgent.disconnect(); - waitFor(wifiCv); - assertEquals(CallbackState.LOST, wifiNetworkCallback.getLastCallback()); - assertEquals(CallbackState.NONE, cellNetworkCallback.getLastCallback()); + wifiNetworkCallback.expectCallback(CallbackState.LOST); + cellNetworkCallback.assertNoCallback(); - cellCv = cellNetworkCallback.getConditionVariable(); - wifiCv = wifiNetworkCallback.getConditionVariable(); mCellNetworkAgent.disconnect(); - waitFor(cellCv); - assertEquals(CallbackState.LOST, cellNetworkCallback.getLastCallback()); - assertEquals(CallbackState.NONE, wifiNetworkCallback.getLastCallback()); + cellNetworkCallback.expectCallback(CallbackState.LOST); + wifiNetworkCallback.assertNoCallback(); } private void tryNetworkFactoryRequests(int capability) throws Exception { @@ -957,18 +1068,21 @@ public class ConnectivityServiceTest extends AndroidTestCase { mServiceContext, "testFactory", filter); testFactory.setScoreFilter(40); ConditionVariable cv = testFactory.getNetworkStartedCV(); + testFactory.expectAddRequests(1); testFactory.register(); + testFactory.waitForNetworkRequests(1); int expectedRequestCount = 1; NetworkCallback networkCallback = null; // For non-INTERNET capabilities we cannot rely on the default request being present, so // add one. if (capability != NET_CAPABILITY_INTERNET) { - testFactory.waitForNetworkRequests(1); assertFalse(testFactory.getMyStartRequested()); NetworkRequest request = new NetworkRequest.Builder().addCapability(capability).build(); networkCallback = new NetworkCallback(); + testFactory.expectAddRequests(1); mCm.requestNetwork(request, networkCallback); expectedRequestCount++; + testFactory.waitForNetworkRequests(expectedRequestCount); } waitFor(cv); assertEquals(expectedRequestCount, testFactory.getMyRequestCount()); @@ -981,13 +1095,20 @@ public class ConnectivityServiceTest extends AndroidTestCase { // unvalidated penalty. testAgent.adjustScore(40); cv = testFactory.getNetworkStoppedCV(); + + // When testAgent connects, ConnectivityService will re-send us all current requests with + // the new score. There are expectedRequestCount such requests, and we must wait for all of + // them. + testFactory.expectAddRequests(expectedRequestCount); testAgent.connect(false); testAgent.addCapability(capability); waitFor(cv); - assertEquals(expectedRequestCount, testFactory.getMyRequestCount()); + testFactory.waitForNetworkRequests(expectedRequestCount); assertFalse(testFactory.getMyStartRequested()); // Bring in a bunch of requests. + testFactory.expectAddRequests(10); + assertEquals(expectedRequestCount, testFactory.getMyRequestCount()); ConnectivityManager.NetworkCallback[] networkCallbacks = new ConnectivityManager.NetworkCallback[10]; for (int i = 0; i< networkCallbacks.length; i++) { @@ -1000,6 +1121,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { assertFalse(testFactory.getMyStartRequested()); // Remove the requests. + testFactory.expectRemoveRequests(10); for (int i = 0; i < networkCallbacks.length; i++) { mCm.unregisterNetworkCallback(networkCallbacks[i]); } @@ -1088,10 +1210,8 @@ public class ConnectivityServiceTest extends AndroidTestCase { // Test bringing up unvalidated cellular with MMS mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS); - cv = networkCallback.getConditionVariable(); mCellNetworkAgent.connectWithoutInternet(); - waitFor(cv); - assertEquals(CallbackState.AVAILABLE, networkCallback.getLastCallback()); + networkCallback.expectCallback(CallbackState.AVAILABLE); verifyActiveNetwork(TRANSPORT_WIFI); // Test releasing NetworkRequest disconnects cellular with MMS cv = mCellNetworkAgent.getDisconnectedCV(); @@ -1114,12 +1234,10 @@ public class ConnectivityServiceTest extends AndroidTestCase { final TestNetworkCallback networkCallback = new TestNetworkCallback(); mCm.requestNetwork(builder.build(), networkCallback); // Test bringing up MMS cellular network - cv = networkCallback.getConditionVariable(); MockNetworkAgent mmsNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mmsNetworkAgent.addCapability(NET_CAPABILITY_MMS); mmsNetworkAgent.connectWithoutInternet(); - waitFor(cv); - assertEquals(CallbackState.AVAILABLE, networkCallback.getLastCallback()); + networkCallback.expectCallback(CallbackState.AVAILABLE); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test releasing MMS NetworkRequest does not disconnect main cellular NetworkAgent cv = mmsNetworkAgent.getDisconnectedCV(); @@ -1139,133 +1257,245 @@ public class ConnectivityServiceTest extends AndroidTestCase { final NetworkRequest validatedRequest = new NetworkRequest.Builder() .addCapability(NET_CAPABILITY_VALIDATED).build(); mCm.registerNetworkCallback(validatedRequest, validatedCallback); - ConditionVariable validatedCv = validatedCallback.getConditionVariable(); // Bring up a network with a captive portal. // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL. - ConditionVariable cv = captivePortalCallback.getConditionVariable(); mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent.connectWithCaptivePortal(); - waitFor(cv); - assertEquals(CallbackState.AVAILABLE, captivePortalCallback.getLastCallback()); + captivePortalCallback.expectCallback(CallbackState.AVAILABLE); // Take down network. // Expect onLost callback. - cv = captivePortalCallback.getConditionVariable(); mWiFiNetworkAgent.disconnect(); - waitFor(cv); - assertEquals(CallbackState.LOST, captivePortalCallback.getLastCallback()); + captivePortalCallback.expectCallback(CallbackState.LOST); // Bring up a network with a captive portal. // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL. - cv = captivePortalCallback.getConditionVariable(); mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent.connectWithCaptivePortal(); - waitFor(cv); - assertEquals(CallbackState.AVAILABLE, captivePortalCallback.getLastCallback()); + captivePortalCallback.expectCallback(CallbackState.AVAILABLE); // Make captive portal disappear then revalidate. // Expect onLost callback because network no longer provides NET_CAPABILITY_CAPTIVE_PORTAL. - cv = captivePortalCallback.getConditionVariable(); mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 204; mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true); - waitFor(cv); - assertEquals(CallbackState.LOST, captivePortalCallback.getLastCallback()); + captivePortalCallback.expectCallback(CallbackState.LOST); // Expect NET_CAPABILITY_VALIDATED onAvailable callback. - waitFor(validatedCv); - assertEquals(CallbackState.AVAILABLE, validatedCallback.getLastCallback()); + validatedCallback.expectCallback(CallbackState.AVAILABLE); // Break network connectivity. // Expect NET_CAPABILITY_VALIDATED onLost callback. - validatedCv = validatedCallback.getConditionVariable(); mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 500; mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false); - waitFor(validatedCv); - assertEquals(CallbackState.LOST, validatedCallback.getLastCallback()); + validatedCallback.expectCallback(CallbackState.LOST); + } + + private static class TestKeepaliveCallback extends PacketKeepaliveCallback { + + public static enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR }; + + private class CallbackValue { + public CallbackType callbackType; + public int error; + + public CallbackValue(CallbackType type) { + this.callbackType = type; + this.error = PacketKeepalive.SUCCESS; + assertTrue("onError callback must have error", type != CallbackType.ON_ERROR); + } + + public CallbackValue(CallbackType type, int error) { + this.callbackType = type; + this.error = error; + assertEquals("error can only be set for onError", type, CallbackType.ON_ERROR); + } + + @Override + public boolean equals(Object o) { + return o instanceof CallbackValue && + this.callbackType == ((CallbackValue) o).callbackType && + this.error == ((CallbackValue) o).error; + } + + @Override + public String toString() { + return String.format("%s(%s, %d)", getClass().getSimpleName(), callbackType, error); + } + } + + private LinkedBlockingQueue<CallbackValue> mCallbacks = new LinkedBlockingQueue<>(); + + @Override + public void onStarted() { + mCallbacks.add(new CallbackValue(CallbackType.ON_STARTED)); + } + + @Override + public void onStopped() { + mCallbacks.add(new CallbackValue(CallbackType.ON_STOPPED)); + } + + @Override + public void onError(int error) { + mCallbacks.add(new CallbackValue(CallbackType.ON_ERROR, error)); + } + + private void expectCallback(CallbackValue callbackValue) { + try { + assertEquals( + callbackValue, + mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + } catch (InterruptedException e) { + fail(callbackValue.callbackType + " callback not seen after " + TIMEOUT_MS + " ms"); + } + } + + public void expectStarted() { + expectCallback(new CallbackValue(CallbackType.ON_STARTED)); + } + + public void expectStopped() { + expectCallback(new CallbackValue(CallbackType.ON_STOPPED)); + } + + public void expectError(int error) { + expectCallback(new CallbackValue(CallbackType.ON_ERROR, error)); + } } -// @Override -// public void tearDown() throws Exception { -// super.tearDown(); -// } -// -// public void testMobileConnectedAddedRoutes() throws Exception { -// Future<?> nextConnBroadcast; -// -// // bring up mobile network -// mMobile.info.setDetailedState(DetailedState.CONNECTED, null, null); -// mMobile.link.setInterfaceName(MOBILE_IFACE); -// mMobile.link.addRoute(MOBILE_ROUTE_V4); -// mMobile.link.addRoute(MOBILE_ROUTE_V6); -// mMobile.doReturnDefaults(); -// -// cv = waitForConnectivityBroadcasts(1); -// mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget(); -// waitFor(cv); -// -// // verify that both routes were added -// int mobileNetId = mMobile.tracker.getNetwork().netId; -// verify(mNetManager).addRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V4)); -// verify(mNetManager).addRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V6)); -// } -// -// public void testMobileWifiHandoff() throws Exception { -// Future<?> nextConnBroadcast; -// -// // bring up mobile network -// mMobile.info.setDetailedState(DetailedState.CONNECTED, null, null); -// mMobile.link.setInterfaceName(MOBILE_IFACE); -// mMobile.link.addRoute(MOBILE_ROUTE_V4); -// mMobile.link.addRoute(MOBILE_ROUTE_V6); -// mMobile.doReturnDefaults(); -// -// cv = waitForConnectivityBroadcasts(1); -// mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget(); -// waitFor(cv); -// -// reset(mNetManager); -// -// // now bring up wifi network -// mWifi.info.setDetailedState(DetailedState.CONNECTED, null, null); -// mWifi.link.setInterfaceName(WIFI_IFACE); -// mWifi.link.addRoute(WIFI_ROUTE_V4); -// mWifi.link.addRoute(WIFI_ROUTE_V6); -// mWifi.doReturnDefaults(); -// -// // expect that mobile will be torn down -// doReturn(true).when(mMobile.tracker).teardown(); -// -// cv = waitForConnectivityBroadcasts(1); -// mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mWifi.info).sendToTarget(); -// waitFor(cv); -// -// // verify that wifi routes added, and teardown requested -// int wifiNetId = mWifi.tracker.getNetwork().netId; -// verify(mNetManager).addRoute(eq(wifiNetId), eq(WIFI_ROUTE_V4)); -// verify(mNetManager).addRoute(eq(wifiNetId), eq(WIFI_ROUTE_V6)); -// verify(mMobile.tracker).teardown(); -// -// int mobileNetId = mMobile.tracker.getNetwork().netId; -// -// reset(mNetManager, mMobile.tracker); -// -// // tear down mobile network, as requested -// mMobile.info.setDetailedState(DetailedState.DISCONNECTED, null, null); -// mMobile.link.clear(); -// mMobile.doReturnDefaults(); -// -// cv = waitForConnectivityBroadcasts(1); -// mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget(); -// waitFor(cv); -// -// verify(mNetManager).removeRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V4)); -// verify(mNetManager).removeRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V6)); -// -// } - - private static InetAddress parse(String addr) { - return InetAddress.parseNumericAddress(addr); + private Network connectKeepaliveNetwork(LinkProperties lp) { + // Ensure the network is disconnected before we do anything. + if (mWiFiNetworkAgent != null) { + assertNull(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork())); + } + + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + ConditionVariable cv = waitForConnectivityBroadcasts(1); + mWiFiNetworkAgent.connect(true); + waitFor(cv); + verifyActiveNetwork(TRANSPORT_WIFI); + mWiFiNetworkAgent.sendLinkProperties(lp); + mService.waitForIdle(); + return mWiFiNetworkAgent.getNetwork(); } + public void testPacketKeepalives() throws Exception { + InetAddress myIPv4 = InetAddress.getByName("192.0.2.129"); + InetAddress notMyIPv4 = InetAddress.getByName("192.0.2.35"); + InetAddress myIPv6 = InetAddress.getByName("2001:db8::1"); + InetAddress dstIPv4 = InetAddress.getByName("8.8.8.8"); + InetAddress dstIPv6 = InetAddress.getByName("2001:4860:4860::8888"); + + LinkProperties lp = new LinkProperties(); + lp.setInterfaceName("wlan12"); + lp.addLinkAddress(new LinkAddress(myIPv6, 64)); + lp.addLinkAddress(new LinkAddress(myIPv4, 25)); + lp.addRoute(new RouteInfo(InetAddress.getByName("fe80::1234"))); + lp.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254"))); + + Network notMyNet = new Network(61234); + Network myNet = connectKeepaliveNetwork(lp); + + TestKeepaliveCallback callback = new TestKeepaliveCallback(); + PacketKeepalive ka; + + // Attempt to start keepalives with invalid parameters and check for errors. + ka = mCm.startNattKeepalive(notMyNet, 25, callback, myIPv4, 1234, dstIPv4); + callback.expectError(PacketKeepalive.ERROR_INVALID_NETWORK); + + ka = mCm.startNattKeepalive(myNet, 19, callback, notMyIPv4, 1234, dstIPv4); + callback.expectError(PacketKeepalive.ERROR_INVALID_INTERVAL); + + ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 1234, dstIPv6); + callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS); + + ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv6, 1234, dstIPv4); + callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS); + + ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv6, 1234, dstIPv6); + callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS); // NAT-T is IPv4-only. + + ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 123456, dstIPv4); + callback.expectError(PacketKeepalive.ERROR_INVALID_PORT); + + ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 123456, dstIPv4); + callback.expectError(PacketKeepalive.ERROR_INVALID_PORT); + + ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4); + callback.expectError(PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED); + + ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4); + callback.expectError(PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED); + + // Check that a started keepalive can be stopped. + mWiFiNetworkAgent.setStartKeepaliveError(PacketKeepalive.SUCCESS); + ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4); + callback.expectStarted(); + mWiFiNetworkAgent.setStopKeepaliveError(PacketKeepalive.SUCCESS); + ka.stop(); + callback.expectStopped(); + + // Check that deleting the IP address stops the keepalive. + LinkProperties bogusLp = new LinkProperties(lp); + ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4); + callback.expectStarted(); + bogusLp.removeLinkAddress(new LinkAddress(myIPv4, 25)); + bogusLp.addLinkAddress(new LinkAddress(notMyIPv4, 25)); + mWiFiNetworkAgent.sendLinkProperties(bogusLp); + callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS); + mWiFiNetworkAgent.sendLinkProperties(lp); + + // Check that a started keepalive is stopped correctly when the network disconnects. + ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4); + callback.expectStarted(); + mWiFiNetworkAgent.disconnect(); + callback.expectError(PacketKeepalive.ERROR_INVALID_NETWORK); + + // ... and that stopping it after that has no adverse effects. + assertNull(mCm.getNetworkCapabilities(myNet)); + ka.stop(); + + // Reconnect. + myNet = connectKeepaliveNetwork(lp); + mWiFiNetworkAgent.setStartKeepaliveError(PacketKeepalive.SUCCESS); + + // Check things work as expected when the keepalive is stopped and the network disconnects. + ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4); + callback.expectStarted(); + ka.stop(); + mWiFiNetworkAgent.disconnect(); + mService.waitForIdle(); + callback.expectStopped(); + + // Reconnect. + myNet = connectKeepaliveNetwork(lp); + mWiFiNetworkAgent.setStartKeepaliveError(PacketKeepalive.SUCCESS); + + // Check that keepalive slots start from 1 and increment. The first one gets slot 1. + mWiFiNetworkAgent.setExpectedKeepaliveSlot(1); + ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4); + callback.expectStarted(); + + // The second one gets slot 2. + mWiFiNetworkAgent.setExpectedKeepaliveSlot(2); + TestKeepaliveCallback callback2 = new TestKeepaliveCallback(); + PacketKeepalive ka2 = mCm.startNattKeepalive(myNet, 25, callback2, myIPv4, 6789, dstIPv4); + callback2.expectStarted(); + + // Now stop the first one and create a third. This also gets slot 1. + ka.stop(); + callback.expectStopped(); + + mWiFiNetworkAgent.setExpectedKeepaliveSlot(1); + TestKeepaliveCallback callback3 = new TestKeepaliveCallback(); + PacketKeepalive ka3 = mCm.startNattKeepalive(myNet, 25, callback3, myIPv4, 9876, dstIPv4); + callback3.expectStarted(); + + ka2.stop(); + callback2.expectStopped(); + + ka3.stop(); + callback3.expectStopped(); + } } diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java index 0f20dde..f9aa124 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java @@ -82,7 +82,7 @@ public class AccountManagerServiceTest extends AndroidTestCase { mAms.addAccountExplicitly(a31, "p31", null); mAms.addAccountExplicitly(a32, "p32", null); - Account[] accounts = mAms.getAccounts(null); + Account[] accounts = mAms.getAccounts(null, mContext.getOpPackageName()); Arrays.sort(accounts, new AccountSorter()); assertEquals(6, accounts.length); assertEquals(a11, accounts[0]); @@ -92,7 +92,7 @@ public class AccountManagerServiceTest extends AndroidTestCase { assertEquals(a22, accounts[4]); assertEquals(a32, accounts[5]); - accounts = mAms.getAccounts("type1" ); + accounts = mAms.getAccounts("type1", mContext.getOpPackageName()); Arrays.sort(accounts, new AccountSorter()); assertEquals(3, accounts.length); assertEquals(a11, accounts[0]); @@ -101,7 +101,7 @@ public class AccountManagerServiceTest extends AndroidTestCase { mAms.removeAccountInternal(a21); - accounts = mAms.getAccounts("type1" ); + accounts = mAms.getAccounts("type1", mContext.getOpPackageName()); Arrays.sort(accounts, new AccountSorter()); assertEquals(2, accounts.length); assertEquals(a11, accounts[0]); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index bd89792..e675c6d 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -267,6 +267,16 @@ public class CarrierConfigManager { "carrier_instant_lettering_available_bool"; /** + * When IMS instant lettering is available for a carrier (see + * {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), determines the list of characters + * which may not be contained in messages. Should be specified as a regular expression suitable + * for use with {@link String#matches(String)}. + * @hide + */ + public static final String KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING = + "carrier_instant_lettering_invalid_chars_string"; + + /** * If Voice Radio Technology is RIL_RADIO_TECHNOLOGY_LTE:14 or RIL_RADIO_TECHNOLOGY_UNKNOWN:0 * this is the value that should be used instead. A configuration value of * RIL_RADIO_TECHNOLOGY_UNKNOWN:0 means there is no replacement value and that the default @@ -371,6 +381,18 @@ public class CarrierConfigManager { public static final String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool"; /** + * Determine whether IMS apn can be shown. + * @hide + */ + public static final String KEY_HIDE_IMS_APN_BOOL = "hide_ims_apn_bool"; + + /** + * Determine whether preferred network type can be shown. + * @hide + */ + public static final String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL = "hide_preferred_network_type_bool"; + + /** * If this is true, the SIM card (through Customer Service Profile EF file) will be able to * prevent manual operator selection. If false, this SIM setting will be ignored and manual * operator selection will always be available. See CPHS4_2.WW6, CPHS B.4.7.1 for more @@ -440,6 +462,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, true); sDefaults.putBoolean(KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL, true); sDefaults.putBoolean(KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL, false); + sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING, ""); sDefaults.putBoolean(KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL, false); sDefaults.putBoolean(KEY_DTMF_TYPE_ENABLED_BOOL, false); sDefaults.putBoolean(KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL, true); @@ -481,6 +504,8 @@ public class CarrierConfigManager { sDefaults.putInt(KEY_IMS_DTMF_TONE_DELAY_INT, 0); sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true); sDefaults.putBoolean(KEY_EDITABLE_ENHANCED_4G_LTE_BOOL, true); + sDefaults.putBoolean(KEY_HIDE_IMS_APN_BOOL, false); + sDefaults.putBoolean(KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL, false); // MMS defaults sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false); |
