Merge "Add carrier config variable to hide ims apn" into mnc-dr-dev
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 @@
@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 @@
@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 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 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 @@
/** @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 @@
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 @@
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 @@
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 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 @@
/** 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 @@
OP_READ_EXTERNAL_STORAGE,
OP_WRITE_EXTERNAL_STORAGE,
OP_TURN_SCREEN_ON,
+ OP_GET_ACCOUNTS,
};
/**
@@ -472,6 +478,7 @@
OPSTR_READ_EXTERNAL_STORAGE,
OPSTR_WRITE_EXTERNAL_STORAGE,
null,
+ OPSTR_GET_ACCOUNTS
};
/**
@@ -541,6 +548,7 @@
"READ_EXTERNAL_STORAGE",
"WRITE_EXTERNAL_STORAGE",
"TURN_ON_SCREEN",
+ "GET_ACCOUNTS",
};
/**
@@ -610,6 +618,7 @@
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 @@
null, // READ_EXTERNAL_STORAGE
null, // WRITE_EXTERNAL_STORAGE
null, // TURN_ON_SCREEN
+ null, // GET_ACCOUNTS
};
/**
@@ -749,6 +759,7 @@
false, // READ_EXTERNAL_STORAGE
false, // WRITE_EXTERNAL_STORAGE
false, // TURN_ON_SCREEN
+ false, // GET_ACCOUNTS
};
/**
@@ -817,6 +828,7 @@
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED, // OP_TURN_ON_SCREEN
+ AppOpsManager.MODE_ALLOWED,
};
/**
@@ -889,6 +901,7 @@
false,
false,
false,
+ false
};
/**
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 @@
/** 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 @@
}
}
- 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 @@
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/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/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 @@
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/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 @@
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 @@
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 @@
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 @@
} else {
EventLogTags.writeSysuiLockscreenGesture(
EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_CAMERA, lengthDp, velocityDp);
- mSecureCameraLaunchManager.startSecureCameraLaunch();
+ mKeyguardBottomArea.launchCamera();
}
mStatusBar.startLaunchTransitionTimeout();
mBlockTouches = true;
@@ -2010,7 +1992,6 @@
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..638cd1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -3535,6 +3535,7 @@
mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
releaseGestureWakeLock();
mNotificationPanel.onAffordanceLaunchEnded();
+ mNotificationPanel.animate().cancel();
mNotificationPanel.setAlpha(1f);
return staying;
}
@@ -3928,7 +3929,7 @@
}
private void vibrateForCameraGesture() {
- mVibrator.vibrate(1000L);
+ mVibrator.vibrate(750L);
}
public void onScreenTurnedOn() {
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 @@
*/
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 @@
}
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 @@
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 @@
}
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/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 8b0e6f2..d590d7a 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.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 @@
private final Context mContext;
private final PackageManager mPackageManager;
+ private final AppOpsManager mAppOpsManager;
private UserManager mUserManager;
private final MessageHandler mMessageHandler;
@@ -266,6 +268,7 @@
IAccountAuthenticatorCache authenticatorCache) {
mContext = context;
mPackageManager = packageManager;
+ mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mMessageHandler = new MessageHandler(FgThread.get().getLooper());
@@ -510,7 +513,7 @@
// 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 @@
// 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 @@
@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 @@
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 @@
* 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 @@
}
@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()
@@ -2602,7 +2609,8 @@
callingUid = packageUid;
}
- 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 +2749,24 @@
}
@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 +2775,16 @@
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 +2797,8 @@
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 +3698,22 @@
}
}
- 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 +3767,13 @@
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 +3785,10 @@
}
}
- 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 +3884,9 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 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 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 @@
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 @@
}
// 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/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.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.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.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 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 @@
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 @@
}
}
+ /**
+ * 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 @@
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 @@
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 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 @@
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();
+ }
}
- protected void releaseNetworkFor(NetworkRequest networkRequest) {
- super.releaseNetworkFor(networkRequest);
- mNetworkReleasedCV.open();
+ @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();
+ }
}
- public ConditionVariable getNetworkRequestedCV() {
- mNetworkRequestedCV.close();
- return mNetworkRequestedCV;
+ private void assertNoExpectations() {
+ if (mExpectations != null) {
+ fail("Can't add expectation, " + mExpectations.getCount() + " already pending");
+ }
}
- public ConditionVariable getNetworkReleasedCV() {
- mNetworkReleasedCV.close();
- return mNetworkReleasedCV;
+ // Expects that count requests will be added.
+ public void expectAddRequests(final int count) {
+ assertNoExpectations();
+ mExpectingAdditions = true;
+ mExpectations = new CountDownLatch(count);
}
- public void waitForNetworkRequests(final int count) {
- waitFor(new Criteria() { public boolean get() { return count == getRequestCount(); } });
+ // Expects that count requests will be removed.
+ public void expectRemoveRequests(final int count) {
+ assertNoExpectations();
+ mExpectingAdditions = false;
+ mExpectations = new CountDownLatch(count);
+ }
+
+ // 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) throws InterruptedException {
+ waitForRequests();
+ assertEquals(count, getMyRequestCount());
}
}
@@ -356,7 +498,6 @@
}
private class WrappedConnectivityService extends ConnectivityService {
- private final ConditionVariable mNetworkMonitorCreated = new ConditionVariable();
private WrappedNetworkMonitor mLastCreatedNetworkMonitor;
public WrappedConnectivityService(Context context, INetworkManagementService netManager,
@@ -365,6 +506,11 @@
}
@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 @@
final WrappedNetworkMonitor monitor = new WrappedNetworkMonitor(context, handler, nai,
defaultRequest);
mLastCreatedNetworkMonitor = monitor;
- mNetworkMonitorCreated.open();
return monitor;
}
@@ -405,10 +550,14 @@
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 @@
}
/**
- * 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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
// 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 @@
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 @@
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);
}
-// @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 class TestKeepaliveCallback extends PacketKeepaliveCallback {
- private static InetAddress parse(String addr) {
- return InetAddress.parseNumericAddress(addr);
+ 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));
+ }
}
+ 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 @@
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 @@
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 @@
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 5ed41c6..135a97a 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -267,6 +267,16 @@
"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
@@ -438,6 +448,7 @@
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);