diff options
115 files changed, 2657 insertions, 1260 deletions
diff --git a/Android.mk b/Android.mk index 534b5e5a5523..d646d9c81a5e 100644 --- a/Android.mk +++ b/Android.mk @@ -63,6 +63,7 @@ LOCAL_SRC_FILES += \ core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl \ core/java/android/accounts/IAccountManager.aidl \ core/java/android/accounts/IAccountManagerResponse.aidl \ + core/java/android/accounts/IAccountAccessTracker.aidl \ core/java/android/accounts/IAccountAuthenticator.aidl \ core/java/android/accounts/IAccountAuthenticatorResponse.aidl \ core/java/android/app/IActivityContainer.aidl \ @@ -221,6 +222,7 @@ LOCAL_SRC_FILES += \ core/java/android/os/IBatteryPropertiesListener.aidl \ core/java/android/os/IBatteryPropertiesRegistrar.aidl \ core/java/android/os/ICancellationSignal.aidl \ + core/java/android/os/IDeviceIdentifiersPolicyService.aidl \ core/java/android/os/IDeviceIdleController.aidl \ core/java/android/os/IHardwarePropertiesManager.aidl \ core/java/android/os/IMaintenanceActivityListener.aidl \ @@ -456,7 +458,7 @@ LOCAL_SRC_FILES += \ wifi/java/android/net/wifi/IWifiManager.aidl \ wifi/java/android/net/wifi/nan/IWifiNanEventCallback.aidl \ wifi/java/android/net/wifi/nan/IWifiNanManager.aidl \ - wifi/java/android/net/wifi/nan/IWifiNanSessionCallback.aidl \ + wifi/java/android/net/wifi/nan/IWifiNanDiscoverySessionCallback.aidl \ wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \ wifi/java/android/net/wifi/IWifiScanner.aidl \ wifi/java/android/net/wifi/IRttManager.aidl \ diff --git a/api/current.txt b/api/current.txt index dea5cec7f60e..25c6ab379bfd 100644 --- a/api/current.txt +++ b/api/current.txt @@ -28398,6 +28398,7 @@ package android.os { public class Build { ctor public Build(); method public static java.lang.String getRadioVersion(); + method public static java.lang.String getSerial(); field public static final java.lang.String BOARD; field public static final java.lang.String BOOTLOADER; field public static final java.lang.String BRAND; @@ -28413,7 +28414,7 @@ package android.os { field public static final java.lang.String MODEL; field public static final java.lang.String PRODUCT; field public static final deprecated java.lang.String RADIO; - field public static final java.lang.String SERIAL; + field public static final deprecated java.lang.String SERIAL; field public static final java.lang.String[] SUPPORTED_32_BIT_ABIS; field public static final java.lang.String[] SUPPORTED_64_BIT_ABIS; field public static final java.lang.String[] SUPPORTED_ABIS; diff --git a/api/system-current.txt b/api/system-current.txt index 0911f0ac08d9..49a976edb36f 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -30888,6 +30888,7 @@ package android.os { public class Build { ctor public Build(); method public static java.lang.String getRadioVersion(); + method public static java.lang.String getSerial(); field public static final java.lang.String BOARD; field public static final java.lang.String BOOTLOADER; field public static final java.lang.String BRAND; @@ -30904,7 +30905,7 @@ package android.os { field public static final boolean PERMISSIONS_REVIEW_REQUIRED; field public static final java.lang.String PRODUCT; field public static final deprecated java.lang.String RADIO; - field public static final java.lang.String SERIAL; + field public static final deprecated java.lang.String SERIAL; field public static final java.lang.String[] SUPPORTED_32_BIT_ABIS; field public static final java.lang.String[] SUPPORTED_64_BIT_ABIS; field public static final java.lang.String[] SUPPORTED_ABIS; diff --git a/api/test-current.txt b/api/test-current.txt index 5c065896cb97..8b0b0e80f0c0 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -28470,6 +28470,7 @@ package android.os { public class Build { ctor public Build(); method public static java.lang.String getRadioVersion(); + method public static java.lang.String getSerial(); field public static final java.lang.String BOARD; field public static final java.lang.String BOOTLOADER; field public static final java.lang.String BRAND; @@ -28485,7 +28486,7 @@ package android.os { field public static final java.lang.String MODEL; field public static final java.lang.String PRODUCT; field public static final deprecated java.lang.String RADIO; - field public static final java.lang.String SERIAL; + field public static final deprecated java.lang.String SERIAL; field public static final java.lang.String[] SUPPORTED_32_BIT_ABIS; field public static final java.lang.String[] SUPPORTED_64_BIT_ABIS; field public static final java.lang.String[] SUPPORTED_ABIS; diff --git a/core/java/android/accounts/Account.java b/core/java/android/accounts/Account.java index 7b83a3076db3..6c16e322e978 100644 --- a/core/java/android/accounts/Account.java +++ b/core/java/android/accounts/Account.java @@ -16,9 +16,17 @@ package android.accounts; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcelable; import android.os.Parcel; +import android.os.RemoteException; import android.text.TextUtils; +import android.util.ArraySet; +import android.util.Log; +import com.android.internal.annotations.GuardedBy; + +import java.util.Set; /** * Value type that represents an Account in the {@link AccountManager}. This object is @@ -26,8 +34,14 @@ import android.text.TextUtils; * suitable for use as the key of a {@link java.util.Map} */ public class Account implements Parcelable { + private static final String TAG = "Account"; + + @GuardedBy("sAccessedAccounts") + private static final Set<Account> sAccessedAccounts = new ArraySet<>(); + public final String name; public final String type; + private final @Nullable IAccountAccessTracker mAccessTracker; public boolean equals(Object o) { if (o == this) return true; @@ -44,6 +58,20 @@ public class Account implements Parcelable { } public Account(String name, String type) { + this(name, type, null); + } + + /** + * @hide + */ + public Account(@NonNull Account other, @Nullable IAccountAccessTracker accessTracker) { + this(other.name, other.type, accessTracker); + } + + /** + * @hide + */ + public Account(String name, String type, IAccountAccessTracker accessTracker) { if (TextUtils.isEmpty(name)) { throw new IllegalArgumentException("the name must not be empty: " + name); } @@ -52,11 +80,29 @@ public class Account implements Parcelable { } this.name = name; this.type = type; + this.mAccessTracker = accessTracker; } public Account(Parcel in) { this.name = in.readString(); this.type = in.readString(); + this.mAccessTracker = IAccountAccessTracker.Stub.asInterface(in.readStrongBinder()); + if (mAccessTracker != null) { + synchronized (sAccessedAccounts) { + if (sAccessedAccounts.add(this)) { + try { + mAccessTracker.onAccountAccessed(); + } catch (RemoteException e) { + Log.e(TAG, "Error noting account access", e); + } + } + } + } + } + + /** @hide */ + public IAccountAccessTracker getAccessTracker() { + return mAccessTracker; } public int describeContents() { @@ -66,6 +112,7 @@ public class Account implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeString(type); + dest.writeStrongInterface(mAccessTracker); } public static final Creator<Account> CREATOR = new Creator<Account>() { diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index 26e7dac587f4..1eb63e0d44ad 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -18,7 +18,6 @@ package android.accounts; import static android.Manifest.permission.GET_ACCOUNTS; -import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.Size; @@ -180,6 +179,14 @@ public class AccountManager { public static final String KEY_ACCOUNT_TYPE = "accountType"; /** + * Bundle key used for the {@link IAccountAccessTracker} account access tracker + * used for noting the account was accessed when unmarshalled from a parcel. + * + * @hide + */ + public static final String KEY_ACCOUNT_ACCESS_TRACKER = "accountAccessTracker"; + + /** * Bundle key used for the auth token value in results * from {@link #getAuthToken} and friends. */ @@ -264,13 +271,13 @@ public class AccountManager { public static final String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator"; /** - * Token for the special case where a UID has access only to an account - * but no authenticator specific auth tokens. + * Token type for the special case where a UID has access only to an account + * but no authenticator specific auth token types. * * @hide */ - public static final String ACCOUNT_ACCESS_TOKEN = - "com.android.abbfd278-af8b-415d-af8b-7571d5dab133"; + public static final String ACCOUNT_ACCESS_TOKEN_TYPE = + "com.android.AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE"; private final Context mContext; private final IAccountManager mService; @@ -919,7 +926,9 @@ public class AccountManager { public Account bundleToResult(Bundle bundle) throws AuthenticatorException { String name = bundle.getString(KEY_ACCOUNT_NAME); String type = bundle.getString(KEY_ACCOUNT_TYPE); - return new Account(name, type); + IAccountAccessTracker tracker = IAccountAccessTracker.Stub.asInterface( + bundle.getBinder(KEY_ACCOUNT_ACCESS_TRACKER)); + return new Account(name, type, tracker); } }.start(); } @@ -2379,6 +2388,7 @@ public class AccountManager { result.putString(KEY_ACCOUNT_NAME, null); result.putString(KEY_ACCOUNT_TYPE, null); result.putString(KEY_AUTHTOKEN, null); + result.putBinder(KEY_ACCOUNT_ACCESS_TRACKER, null); try { mResponse.onResult(result); } catch (RemoteException e) { @@ -2404,9 +2414,13 @@ public class AccountManager { public void onResult(Bundle value) throws RemoteException { Account account = new Account( value.getString(KEY_ACCOUNT_NAME), - value.getString(KEY_ACCOUNT_TYPE)); - mFuture = getAuthToken(account, mAuthTokenType, mLoginOptions, - mActivity, mMyCallback, mHandler); + value.getString(KEY_ACCOUNT_TYPE), + IAccountAccessTracker.Stub.asInterface( + value.getBinder( + KEY_ACCOUNT_ACCESS_TRACKER))); + mFuture = getAuthToken(account, mAuthTokenType, + mLoginOptions, mActivity, mMyCallback, + mHandler); } @Override @@ -2453,7 +2467,9 @@ public class AccountManager { setException(new AuthenticatorException("account not in result")); return; } - final Account account = new Account(accountName, accountType); + final IAccountAccessTracker tracker = IAccountAccessTracker.Stub.asInterface( + result.getBinder(KEY_ACCOUNT_ACCESS_TRACKER)); + final Account account = new Account(accountName, accountType, tracker); mNumAccounts = 1; getAuthToken(account, mAuthTokenType, null /* options */, mActivity, mMyCallback, mHandler); diff --git a/core/java/android/accounts/AccountManagerInternal.java b/core/java/android/accounts/AccountManagerInternal.java index d777643950e6..68c17c32fdc3 100644 --- a/core/java/android/accounts/AccountManagerInternal.java +++ b/core/java/android/accounts/AccountManagerInternal.java @@ -28,6 +28,21 @@ import android.os.RemoteCallback; public abstract class AccountManagerInternal { /** + * Listener for explicit UID account access grant changes. + */ + public interface OnAppPermissionChangeListener { + + /** + * Called when the explicit grant state for a given UID to + * access an account changes. + * + * @param account The account + * @param uid The UID for which the grant changed + */ + public void onAppPermissionChanged(Account account, int uid); + } + + /** * Requests that a given package is given access to an account. * The provided callback will be invoked with a {@link android.os.Bundle} * containing the result which will be a boolean value mapped to the @@ -38,7 +53,38 @@ public abstract class AccountManagerInternal { * @param userId Concrete user id for which to request. * @param callback A callback for receiving the result. */ - public abstract void requestAccountAccess(@NonNull Account account, + public abstract void requestAccountAccess(@NonNull Account account, @NonNull String packageName, @IntRange(from = 0) int userId, @NonNull RemoteCallback callback); + + /** + * Check whether the given UID has access to the account. + * + * @param account The account + * @param uid The UID + * @return Whether the UID can access the account + */ + public abstract boolean hasAccountAccess(@NonNull Account account, @IntRange(from = 0) int uid); + + /** + * Adds a listener for explicit UID account access grant changes. + * + * @param listener The listener + */ + public abstract void addOnAppPermissionChangeListener( + @NonNull OnAppPermissionChangeListener listener); + + /** + * Backups the account access permissions. + * @param userId The user for which to backup. + * @return The backup data. + */ + public abstract byte[] backupAccountAccessPermissions(int userId); + + /** + * Restores the account access permissions. + * @param data The restore data. + * @param userId The user for which to restore. + */ + public abstract void restoreAccountAccessPermissions(byte[] data, int userId); } diff --git a/core/java/android/accounts/GrantCredentialsPermissionActivity.java b/core/java/android/accounts/GrantCredentialsPermissionActivity.java index 8d0ce58d3358..38eab2905022 100644 --- a/core/java/android/accounts/GrantCredentialsPermissionActivity.java +++ b/core/java/android/accounts/GrantCredentialsPermissionActivity.java @@ -108,7 +108,7 @@ public class GrantCredentialsPermissionActivity extends Activity implements View } }; - if (!AccountManager.ACCOUNT_ACCESS_TOKEN.equals(mAuthTokenType)) { + if (!AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE.equals(mAuthTokenType)) { AccountManager.get(this).getAuthTokenLabel(mAccount.type, mAuthTokenType, callback, null); } diff --git a/core/java/android/accounts/IAccountAccessTracker.aidl b/core/java/android/accounts/IAccountAccessTracker.aidl new file mode 100644 index 000000000000..e12b3d1e21c7 --- /dev/null +++ b/core/java/android/accounts/IAccountAccessTracker.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2016 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.accounts; + +/** + * Interface to track which apps accessed an account + * + * @hide + */ +oneway interface IAccountAccessTracker { + void onAccountAccessed(); +} diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index cd4ba7ce2817..a0408c93c724 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -128,6 +128,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.lang.ref.WeakReference; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.net.InetAddress; import java.text.DateFormat; import java.util.ArrayList; @@ -519,6 +521,7 @@ public final class ActivityThread { boolean persistent; Configuration config; CompatibilityInfo compatInfo; + String buildSerial; /** Initial values for {@link Profiler}. */ ProfilerInfo initProfilerInfo; @@ -855,7 +858,8 @@ public final class ActivityThread { IUiAutomationConnection instrumentationUiConnection, int debugMode, boolean enableBinderTracking, boolean trackAllocation, boolean isRestrictedBackupMode, boolean persistent, Configuration config, - CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings) { + CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings, + String buildSerial) { if (services != null) { // Setup the service cache in the ServiceManager @@ -880,6 +884,7 @@ public final class ActivityThread { data.config = config; data.compatInfo = compatInfo; data.initProfilerInfo = profilerInfo; + data.buildSerial = buildSerial; sendMessage(H.BIND_APPLICATION, data); } @@ -5196,6 +5201,18 @@ public final class ActivityThread { StrictMode.enableDeathOnFileUriExposure(); } + // We deprecated Build.SERIAL and only apps that target pre NMR1 + // SDK can see it. Since access to the serial is now behind a + // permission we push down the value and here we fix it up + // before any app code has been loaded. + try { + Field field = Build.class.getDeclaredField("SERIAL"); + field.setAccessible(true); + field.set(Build.class, data.buildSerial); + } catch (NoSuchFieldException | IllegalAccessException e) { + /* ignore */ + } + if (data.debugMode != IApplicationThread.DEBUG_OFF) { // XXX should have option to change the port. Debug.changeDebugPort(8100); diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index 28899e269fbe..12e527ea4841 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -302,10 +302,11 @@ public abstract class ApplicationThreadNative extends Binder CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data); HashMap<String, IBinder> services = data.readHashMap(null); Bundle coreSettings = data.readBundle(); + String buildSerial = data.readString(); bindApplication(packageName, info, providers, testName, profilerInfo, testArgs, testWatcher, uiAutomationConnection, testMode, enableBinderTracking, trackAllocation, restrictedBackupMode, persistent, config, compatInfo, services, - coreSettings); + coreSettings, buildSerial); return true; } @@ -1060,7 +1061,8 @@ class ApplicationThreadProxy implements IApplicationThread { IUiAutomationConnection uiAutomationConnection, int debugMode, boolean enableBinderTracking, boolean trackAllocation, boolean restrictedBackupMode, boolean persistent, Configuration config, CompatibilityInfo compatInfo, - Map<String, IBinder> services, Bundle coreSettings) throws RemoteException { + Map<String, IBinder> services, Bundle coreSettings, String buildSerial) + throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); data.writeString(packageName); @@ -1090,6 +1092,7 @@ class ApplicationThreadProxy implements IApplicationThread { compatInfo.writeToParcel(data, 0); data.writeMap(services); data.writeBundle(coreSettings); + data.writeString(buildSerial); mRemote.transact(BIND_APPLICATION_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); data.recycle(); diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index 131c1f9be685..4189dd96d3f9 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -99,8 +99,8 @@ public interface IApplicationThread extends IInterface { IInstrumentationWatcher testWatcher, IUiAutomationConnection uiAutomationConnection, int debugMode, boolean enableBinderTracking, boolean trackAllocation, boolean restrictedBackupMode, boolean persistent, Configuration config, - CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings) - throws RemoteException; + CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings, + String buildSerial) throws RemoteException; void scheduleExit() throws RemoteException; void scheduleSuicide() throws RemoteException; void scheduleConfigurationChanged(Configuration config) throws RemoteException; diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java index 444e4293fe2b..a395aa470f5b 100644 --- a/core/java/android/bluetooth/BluetoothAvrcpController.java +++ b/core/java/android/bluetooth/BluetoothAvrcpController.java @@ -66,21 +66,6 @@ public final class BluetoothAvrcpController implements BluetoothProfile { "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED"; /** - * Intent used to broadcast the change in metadata state of playing track on the AVRCP - * AG. - * - * <p>This intent will have the two extras: - * <ul> - * <li> {@link #EXTRA_METADATA} - {@link MediaMetadata} containing the current metadata.</li> - * <li> {@link #EXTRA_PLAYBACK} - {@link PlaybackState} containing the current playback - * state. </li> - * </ul> - */ - public static final String ACTION_TRACK_EVENT = - "android.bluetooth.avrcp-controller.profile.action.TRACK_EVENT"; - - - /** * Intent used to broadcast the change in player application setting state on AVRCP AG. * * <p>This intent will have the following extras: @@ -92,35 +77,9 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public static final String ACTION_PLAYER_SETTING = "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING"; - public static final String EXTRA_METADATA = - "android.bluetooth.avrcp-controller.profile.extra.METADATA"; - - public static final String EXTRA_PLAYBACK = - "android.bluetooth.avrcp-controller.profile.extra.PLAYBACK"; - public static final String EXTRA_PLAYER_SETTING = "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING"; - /* - * KeyCoded for Pass Through Commands - */ - public static final int PASS_THRU_CMD_ID_PLAY = 0x44; - public static final int PASS_THRU_CMD_ID_PAUSE = 0x46; - public static final int PASS_THRU_CMD_ID_VOL_UP = 0x41; - public static final int PASS_THRU_CMD_ID_VOL_DOWN = 0x42; - public static final int PASS_THRU_CMD_ID_STOP = 0x45; - public static final int PASS_THRU_CMD_ID_FF = 0x49; - public static final int PASS_THRU_CMD_ID_REWIND = 0x48; - public static final int PASS_THRU_CMD_ID_FORWARD = 0x4B; - public static final int PASS_THRU_CMD_ID_BACKWARD = 0x4C; - /* Key State Variables */ - public static final int KEY_STATE_PRESSED = 0; - public static final int KEY_STATE_RELEASED = 1; - /* Group Navigation Key Codes */ - public static final int PASS_THRU_CMD_ID_NEXT_GRP = 0x00; - public static final int PASS_THRU_CMD_ID_PREV_GRP = 0x01; - - private Context mContext; private ServiceListener mServiceListener; private IBluetoothAvrcpController mService; @@ -267,20 +226,6 @@ public final class BluetoothAvrcpController implements BluetoothProfile { return BluetoothProfile.STATE_DISCONNECTED; } - public void sendPassThroughCmd(BluetoothDevice device, int keyCode, int keyState) { - if (DBG) Log.d(TAG, "sendPassThroughCmd"); - if (mService != null && isEnabled()) { - try { - mService.sendPassThroughCmd(device, keyCode, keyState); - return; - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in sendPassThroughCmd()", e); - return; - } - } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); - } - /** * Gets the player application settings. * @@ -301,49 +246,6 @@ public final class BluetoothAvrcpController implements BluetoothProfile { } /** - * Gets the metadata for the current track. - * - * This should be usually called when application UI needs to be updated, eg. when the track - * changes or immediately after connecting and getting the current state. - * @return the {@link MediaMetadata} or {@link null} if there is an error. - */ - public MediaMetadata getMetadata(BluetoothDevice device) { - if (DBG) Log.d(TAG, "getMetadata"); - MediaMetadata metadata = null; - if (mService != null && isEnabled()) { - try { - metadata = mService.getMetadata(device); - } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in getMetadata() " + e); - return null; - } - } - return metadata; - } - - /** - * Gets the playback state for current track. - * - * When the application is first connecting it can use current track state to get playback info. - * For all further updates it should listen to notifications. - * @return the {@link PlaybackState} or {@link null} if there is an error. - */ - public PlaybackState getPlaybackState(BluetoothDevice device) { - if (DBG) Log.d(TAG, "getPlaybackState"); - PlaybackState playbackState = null; - if (mService != null && isEnabled()) { - try { - playbackState = mService.getPlaybackState(device); - } catch (RemoteException e) { - Log.e(TAG, - "Error talking to BT service in getPlaybackState() " + e); - return null; - } - } - return playbackState; - } - - /** * Sets the player app setting for current player. * returns true in case setting is supported by remote, false otherwise */ diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 189147efa829..6d6dfebced2c 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -1167,12 +1167,12 @@ public final class BluetoothDevice implements Parcelable { /** * Confirm passkey for {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION} pairing. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. * * @return true confirmation has been sent out * false for error */ - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPairingConfirmation(boolean confirm) { if (sService == null) { Log.e(TAG, "BT not enabled. Cannot set pairing confirmation"); diff --git a/core/java/android/bluetooth/IBluetoothAvrcpController.aidl b/core/java/android/bluetooth/IBluetoothAvrcpController.aidl index f1288d022290..cfa11cac4a8a 100644 --- a/core/java/android/bluetooth/IBluetoothAvrcpController.aidl +++ b/core/java/android/bluetooth/IBluetoothAvrcpController.aidl @@ -30,10 +30,7 @@ interface IBluetoothAvrcpController { List<BluetoothDevice> getConnectedDevices(); List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states); int getConnectionState(in BluetoothDevice device); - void sendPassThroughCmd(in BluetoothDevice device, int keyCode, int keyState); BluetoothAvrcpPlayerSettings getPlayerSettings(in BluetoothDevice device); - MediaMetadata getMetadata(in BluetoothDevice device); - PlaybackState getPlaybackState(in BluetoothDevice device); boolean setPlayerApplicationSetting(in BluetoothAvrcpPlayerSettings plAppSetting); void sendGroupNavigationCmd(in BluetoothDevice device, int keyCode, int keyState); } diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index b3320d6f6976..daa1b93889cc 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -1877,6 +1877,7 @@ public abstract class ContentResolver { if (extras != null) { String accountName = extras.getString(SYNC_EXTRAS_ACCOUNT); if (!TextUtils.isEmpty(accountName)) { + // TODO: No references to Google in AOSP account = new Account(accountName, "com.google"); } extras.remove(SYNC_EXTRAS_ACCOUNT); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index b295919f6af9..457866b24aab 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3675,6 +3675,12 @@ public abstract class Context { public static final String GATEKEEPER_SERVICE = "android.service.gatekeeper.IGateKeeperService"; /** + * Service defining the policy for access to device identifiers. + * @hide + */ + public static final String DEVICE_IDENTIFIERS_SERVICE = "device_identifiers"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index d208fe7c10ca..f5bcf64417a6 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -160,4 +160,12 @@ public abstract class PackageManagerInternal { * Returns {@code true} if a given package can't be wiped. Otherwise, returns {@code false}. */ public abstract boolean isPackageDataProtected(int userId, String packageName); + + /** + * Gets whether the package was ever launched. + * @param packageName The package name. + * @param userId The user for which to check. + * @return Whether was launched. + */ + public abstract boolean wasPackageEverLaunched(String packageName, int userId); } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index fd9e60d74215..c793a6eadf49 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -3241,6 +3241,27 @@ public class ConnectivityManager { } /** + * Informs the system to penalize {@code network}'s score when it becomes unvalidated. This is + * only meaningful if the system is configured not to penalize such networks, e.g., if the + * {@code config_networkAvoidBadWifi} configuration variable is set to 0 and the {@code + * NETWORK_AVOID_BAD_WIFI setting is unset}. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL} + * + * @param network The network to accept. + * + * @hide + */ + public void setAvoidUnvalidated(Network network) { + try { + mService.setAvoidUnvalidated(network); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Resets all connectivity manager settings back to factory defaults. * @hide */ diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index d48c155986f3..4aabda9eb09d 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -161,6 +161,7 @@ interface IConnectivityManager void releaseNetworkRequest(in NetworkRequest networkRequest); void setAcceptUnvalidated(in Network network, boolean accept, boolean always); + void setAvoidUnvalidated(in Network network); int getRestoreDefaultNetworkDelay(int networkType); diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java index 8cdd15305018..ea53a71245a5 100644 --- a/core/java/android/net/InterfaceConfiguration.java +++ b/core/java/android/net/InterfaceConfiguration.java @@ -80,6 +80,14 @@ public class InterfaceConfiguration implements Parcelable { mFlags.add(FLAG_DOWN); } + /** + * Set flags so that no changes will be made to the up/down status. + */ + public void ignoreInterfaceUpDownStatus() { + mFlags.remove(FLAG_UP); + mFlags.remove(FLAG_DOWN); + } + public LinkAddress getLinkAddress() { return mAddr; } diff --git a/core/java/android/net/metrics/DnsEvent.java b/core/java/android/net/metrics/DnsEvent.java index 4fc6b7aaf842..6176b2c58deb 100644 --- a/core/java/android/net/metrics/DnsEvent.java +++ b/core/java/android/net/metrics/DnsEvent.java @@ -21,7 +21,7 @@ import android.os.Parcel; import android.os.Parcelable; /** - * An event recorded by DnsEventListenerService. + * A DNS event recorded by NetdEventListenerService. * {@hide} */ @SystemApi diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 5b51002120d6..01915895c229 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -16,7 +16,10 @@ package android.os; +import android.Manifest; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.content.Context; import android.text.TextUtils; import android.util.Slog; @@ -98,8 +101,35 @@ public class Build { */ public static final boolean IS_EMULATOR = getString("ro.kernel.qemu").equals("1"); - /** A hardware serial number, if available. Alphanumeric only, case-insensitive. */ - public static final String SERIAL = getString("ro.serialno"); + /** + * A hardware serial number, if available. Alphanumeric only, case-insensitive. + * For apps targeting SDK higher than {@link Build.VERSION_CODES#N_MR1} this + * field is set to {@link Build#UNKNOWN}. + * + * @deprecated Use {@link #getSerial()} instead. + **/ + @Deprecated + // IMPORTANT: This field should be initialized via a function call to + // prevent its value being inlined in the app during compilation because + // we will later set it to the value based on the app's target SDK. + public static final String SERIAL = getString("no.such.thing"); + + /** + * Gets the hardware serial, if available. Requires holding the {@link + * android.Manifest.permission#READ_PHONE_STATE} permission. + * @return The serial if specified. + */ + @RequiresPermission(Manifest.permission.READ_PHONE_STATE) + public static String getSerial() { + IDeviceIdentifiersPolicyService service = IDeviceIdentifiersPolicyService.Stub + .asInterface(ServiceManager.getService(Context.DEVICE_IDENTIFIERS_SERVICE)); + try { + return service.getSerial(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + return UNKNOWN; + } /** * An ordered list of ABIs supported by this device. The most preferred ABI is the first diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java index e4d57184ede9..180e8f46e93b 100644 --- a/core/java/android/os/HwParcel.java +++ b/core/java/android/os/HwParcel.java @@ -53,21 +53,13 @@ public class HwParcel { public native final void writeDouble(double val); public native final void writeString(String val); - public native final void writeBoolArray(int size, boolean[] val); public native final void writeBoolVector(boolean[] val); - public native final void writeInt8Array(int size, byte[] val); public native final void writeInt8Vector(byte[] val); - public native final void writeInt16Array(int size, short[] val); public native final void writeInt16Vector(short[] val); - public native final void writeInt32Array(int size, int[] val); public native final void writeInt32Vector(int[] val); - public native final void writeInt64Array(int size, long[] val); public native final void writeInt64Vector(long[] val); - public native final void writeFloatArray(int size, float[] val); public native final void writeFloatVector(float[] val); - public native final void writeDoubleArray(int size, double[] val); public native final void writeDoubleVector(double[] val); - public native final void writeStringArray(int size, String[] val); public native final void writeStringVector(String[] val); public native final void writeStrongBinder(IHwBinder binder); @@ -82,21 +74,13 @@ public class HwParcel { public native final double readDouble(); public native final String readString(); - public native final boolean[] readBoolArray(int size); public native final boolean[] readBoolVector(); - public native final byte[] readInt8Array(int size); public native final byte[] readInt8Vector(); - public native final short[] readInt16Array(int size); public native final short[] readInt16Vector(); - public native final int[] readInt32Array(int size); public native final int[] readInt32Vector(); - public native final long[] readInt64Array(int size); public native final long[] readInt64Vector(); - public native final float[] readFloatArray(int size); public native final float[] readFloatVector(); - public native final double[] readDoubleArray(int size); public native final double[] readDoubleVector(); - public native final String[] readStringArray(int size); public native final String[] readStringVector(); public native final IHwBinder readStrongBinder(); diff --git a/core/java/android/os/IDeviceIdentifiersPolicyService.aidl b/core/java/android/os/IDeviceIdentifiersPolicyService.aidl new file mode 100644 index 000000000000..ac19f2bd5bbc --- /dev/null +++ b/core/java/android/os/IDeviceIdentifiersPolicyService.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2016 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.os; + +/** + * @hide + */ +interface IDeviceIdentifiersPolicyService { + String getSerial(); +}
\ No newline at end of file diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index ce7a1243fe7a..1aed9b322816 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -1358,5 +1358,34 @@ public final class PowerManager { + " held=" + mHeld + ", refCount=" + mCount + "}"; } } + + /** + * Wraps a Runnable such that this method immediately acquires the wake lock and then + * once the Runnable is done the wake lock is released. + * + * <p>Example: + * + * <pre> + * mHandler.post(mWakeLock.wrap(() -> { + * // do things on handler, lock is held while we're waiting for this + * // to get scheduled and until the runnable is done executing. + * }); + * </pre> + * + * <p>Note: you must make sure that the Runnable eventually gets executed, otherwise you'll + * leak the wakelock! + * + * @hide + */ + public Runnable wrap(Runnable r) { + acquire(); + return () -> { + try { + r.run(); + } finally { + release(); + } + }; + } } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index f8cf9ab7b307..12a76c5d4875 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5989,6 +5989,18 @@ public final class Settings { public static final String DOZE_ENABLED = "doze_enabled"; /** + * Whether the device should pulse on pick up gesture. + * @hide + */ + public static final String DOZE_PULSE_ON_PICK_UP = "doze_pulse_on_pick_up"; + + /** + * Whether the device should pulse on double tap gesture. + * @hide + */ + public static final String DOZE_PULSE_ON_DOUBLE_TAP = "doze_pulse_on_double_tap"; + + /** * The current night mode that has been selected by the user. Owned * and controlled by UiModeManagerService. Constants are as per * UiModeManager. @@ -6517,6 +6529,9 @@ public final class Settings { CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, SYSTEM_NAVIGATION_KEYS_ENABLED, QS_TILES, + DOZE_ENABLED, + DOZE_PULSE_ON_PICK_UP, + DOZE_PULSE_ON_DOUBLE_TAP }; /** @@ -7559,6 +7574,13 @@ public final class Settings { /** * Whether to automatically switch away from wifi networks that lose Internet access. + * Only meaningful if config_networkAvoidBadWifi is set to 0, otherwise the system always + * avoids such networks. Valid values are: + * + * 0: Don't avoid bad wifi, don't prompt the user. Get stuck on bad wifi like it's 2013. + * null: Ask the user whether to switch away from bad wifi. + * 1: Avoid bad wifi. + * * @hide */ public static final String NETWORK_AVOID_BAD_WIFI = "network_avoid_bad_wifi"; diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 0557d138eb74..e958fbef563d 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -192,6 +192,9 @@ public class DreamService extends Service implements Window.Callback { private boolean mDebug = false; + private PowerManager.WakeLock mWakeLock; + private boolean mWakeLockAcquired; + public DreamService() { mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE)); } @@ -786,6 +789,8 @@ public class DreamService extends Service implements Window.Callback { public void onCreate() { if (mDebug) Slog.v(TAG, "onCreate()"); super.onCreate(); + mWakeLock = getSystemService(PowerManager.class) + .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "DreamService"); } /** @@ -825,9 +830,21 @@ public class DreamService extends Service implements Window.Callback { @Override public final IBinder onBind(Intent intent) { if (mDebug) Slog.v(TAG, "onBind() intent = " + intent); + + // Need to stay awake until we dispatch onDreamingStarted. This is released either in + // attach() or onDestroy(). + mWakeLock.acquire(5000); + mWakeLockAcquired = true; return new DreamServiceWrapper(); } + private void releaseWakeLockIfNeeded() { + if (mWakeLockAcquired) { + mWakeLock.release(); + mWakeLockAcquired = false; + } + } + /** * Stops the dream and detaches from the window. * <p> @@ -904,6 +921,8 @@ public class DreamService extends Service implements Window.Callback { detach(); super.onDestroy(); + + releaseWakeLockIfNeeded(); // for acquire in onBind() } // end public api @@ -944,83 +963,88 @@ public class DreamService extends Service implements Window.Callback { * @param windowToken A window token that will allow a window to be created in the correct layer. */ private final void attach(IBinder windowToken, boolean canDoze) { - if (mWindowToken != null) { - Slog.e(TAG, "attach() called when already attached with token=" + mWindowToken); - return; - } - if (mFinished || mWaking) { - Slog.w(TAG, "attach() called after dream already finished"); - try { - mSandman.finishSelf(windowToken, true /*immediate*/); - } catch (RemoteException ex) { - // system server died + try { + if (mWindowToken != null) { + Slog.e(TAG, "attach() called when already attached with token=" + mWindowToken); + return; + } + if (mFinished || mWaking) { + Slog.w(TAG, "attach() called after dream already finished"); + try { + mSandman.finishSelf(windowToken, true /*immediate*/); + } catch (RemoteException ex) { + // system server died + } + return; } - return; - } - - mWindowToken = windowToken; - mCanDoze = canDoze; - if (mWindowless && !mCanDoze) { - throw new IllegalStateException("Only doze dreams can be windowless"); - } - if (!mWindowless) { - mWindow = new PhoneWindow(this); - mWindow.setCallback(this); - mWindow.requestFeature(Window.FEATURE_NO_TITLE); - mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000)); - mWindow.setFormat(PixelFormat.OPAQUE); - - if (mDebug) Slog.v(TAG, String.format("Attaching window token: %s to window of type %s", - windowToken, WindowManager.LayoutParams.TYPE_DREAM)); - WindowManager.LayoutParams lp = mWindow.getAttributes(); - lp.type = WindowManager.LayoutParams.TYPE_DREAM; - lp.token = windowToken; - lp.windowAnimations = com.android.internal.R.style.Animation_Dream; - lp.flags |= ( WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR - | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED - | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD - | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON - | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0) - | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0) - ); - mWindow.setAttributes(lp); - // Workaround: Currently low-profile and in-window system bar backgrounds don't go - // along well. Dreams usually don't need such bars anyways, so disable them by default. - mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); - mWindow.setWindowManager(null, windowToken, "dream", true); + mWindowToken = windowToken; + mCanDoze = canDoze; + if (mWindowless && !mCanDoze) { + throw new IllegalStateException("Only doze dreams can be windowless"); + } + if (!mWindowless) { + mWindow = new PhoneWindow(this); + mWindow.setCallback(this); + mWindow.requestFeature(Window.FEATURE_NO_TITLE); + mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000)); + mWindow.setFormat(PixelFormat.OPAQUE); + + if (mDebug) { + Slog.v(TAG, String.format("Attaching window token: %s to window of type %s", + windowToken, WindowManager.LayoutParams.TYPE_DREAM)); + } - applySystemUiVisibilityFlags( - (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0), - View.SYSTEM_UI_FLAG_LOW_PROFILE); + WindowManager.LayoutParams lp = mWindow.getAttributes(); + lp.type = WindowManager.LayoutParams.TYPE_DREAM; + lp.token = windowToken; + lp.windowAnimations = com.android.internal.R.style.Animation_Dream; + lp.flags |= (WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR + | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED + | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD + | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON + | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0) + | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0) + ); + mWindow.setAttributes(lp); + // Workaround: Currently low-profile and in-window system bar backgrounds don't go + // along well. Dreams usually don't need such bars anyways, so disable them by default. + mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + mWindow.setWindowManager(null, windowToken, "dream", true); + + applySystemUiVisibilityFlags( + (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0), + View.SYSTEM_UI_FLAG_LOW_PROFILE); - try { - getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes()); - } catch (WindowManager.BadTokenException ex) { - // This can happen because the dream manager service will remove the token - // immediately without necessarily waiting for the dream to start. - // We should receive a finish message soon. - Slog.i(TAG, "attach() called after window token already removed, dream will " - + "finish soon"); - mWindow = null; - return; + try { + getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes()); + } catch (WindowManager.BadTokenException ex) { + // This can happen because the dream manager service will remove the token + // immediately without necessarily waiting for the dream to start. + // We should receive a finish message soon. + Slog.i(TAG, "attach() called after window token already removed, dream will " + + "finish soon"); + mWindow = null; + return; + } } - } - // We need to defer calling onDreamingStarted until after onWindowAttached, - // which is posted to the handler by addView, so we post onDreamingStarted - // to the handler also. Need to watch out here in case detach occurs before - // this callback is invoked. - mHandler.post(new Runnable() { - @Override - public void run() { + // We need to defer calling onDreamingStarted until after onWindowAttached, + // which is posted to the handler by addView, so we post onDreamingStarted + // to the handler also. Need to watch out here in case detach occurs before + // this callback is invoked. + mHandler.post(mWakeLock.wrap(() -> { if (mWindow != null || mWindowless) { - if (mDebug) Slog.v(TAG, "Calling onDreamingStarted()"); + if (mDebug) { + Slog.v(TAG, "Calling onDreamingStarted()"); + } mStarted = true; onDreamingStarted(); } - } - }); + })); + } finally { + releaseWakeLockIfNeeded(); // for acquire in onBind + } } private boolean getWindowFlagValue(int flag, boolean defaultValue) { diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java index 16f3bc050a89..479f49368191 100644 --- a/core/java/android/transition/TransitionManager.java +++ b/core/java/android/transition/TransitionManager.java @@ -185,25 +185,25 @@ public class TransitionManager { final ViewGroup sceneRoot = scene.getSceneRoot(); if (!sPendingTransitions.contains(sceneRoot)) { - sPendingTransitions.add(sceneRoot); + if (transition == null) { + scene.enter(); + } else { + sPendingTransitions.add(sceneRoot); - Transition transitionClone = null; - if (transition != null) { - transitionClone = transition.clone(); + Transition transitionClone = transition.clone(); transitionClone.setSceneRoot(sceneRoot); - } - Scene oldScene = Scene.getCurrentScene(sceneRoot); - if (oldScene != null && transitionClone != null && - oldScene.isCreatedFromLayoutResource()) { - transitionClone.setCanRemoveViews(true); - } + Scene oldScene = Scene.getCurrentScene(sceneRoot); + if (oldScene != null && oldScene.isCreatedFromLayoutResource()) { + transitionClone.setCanRemoveViews(true); + } - sceneChangeSetup(sceneRoot, transitionClone); + sceneChangeSetup(sceneRoot, transitionClone); - scene.enter(); + scene.enter(); - sceneChangeRunTransition(sceneRoot, transitionClone); + sceneChangeRunTransition(sceneRoot, transitionClone); + } } } diff --git a/core/java/android/util/PackageUtils.java b/core/java/android/util/PackageUtils.java new file mode 100644 index 000000000000..6531aef9a50c --- /dev/null +++ b/core/java/android/util/PackageUtils.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2016 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.util; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.Signature; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * Helper functions applicable to packages. + * @hide + */ +public final class PackageUtils { + private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); + + private PackageUtils() { + /* hide constructor */ + } + + /** + * Computes the SHA256 digest of the signing cert for a package. + * @param packageManager The package manager. + * @param packageName The package for which to generate the digest. + * @param userId The user for which to generate the digest. + * @return The digest or null if the package does not exist for this user. + */ + public static @Nullable String computePackageCertSha256Digest( + @NonNull PackageManager packageManager, + @NonNull String packageName, int userId) { + final PackageInfo packageInfo; + try { + packageInfo = packageManager.getPackageInfoAsUser(packageName, + PackageManager.GET_SIGNATURES, userId); + } catch (PackageManager.NameNotFoundException e) { + return null; + } + return computeCertSha256Digest(packageInfo.signatures[0]); + } + + /** + * Computes the SHA256 digest of a cert. + * @param signature The signature. + * @return The digest or null if an error occurs. + */ + public static @Nullable String computeCertSha256Digest(@NonNull Signature signature) { + return computeSha256Digest(signature.toByteArray()); + } + + /** + * Computes the SHA256 digest of some data. + * @param data The data. + * @return The digest or null if an error occurs. + */ + public static @Nullable String computeSha256Digest(@NonNull byte[] data) { + MessageDigest messageDigest; + try { + messageDigest = MessageDigest.getInstance("SHA256"); + } catch (NoSuchAlgorithmException e) { + /* can't happen */ + return null; + } + + messageDigest.update(data); + + final byte[] digest = messageDigest.digest(); + final int digestLength = digest.length; + final int charCount = 2 * digestLength; + + final char[] chars = new char[charCount]; + for (int i = 0; i < digestLength; i++) { + final int byteHex = digest[i] & 0xFF; + chars[i * 2] = HEX_ARRAY[byteHex >>> 4]; + chars[i * 2 + 1] = HEX_ARRAY[byteHex & 0x0F]; + } + return new String(chars); + } +} diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java index a394f351b7f3..691a385be9da 100644 --- a/core/java/android/view/DragEvent.java +++ b/core/java/android/view/DragEvent.java @@ -134,6 +134,7 @@ public class DragEvent implements Parcelable { Object mLocalState; boolean mDragResult; + boolean mEventHandlerWasCalled; private DragEvent mNext; private RuntimeException mRecycledLocation; @@ -439,6 +440,7 @@ public class DragEvent implements Parcelable { mClipData = null; mClipDescription = null; mLocalState = null; + mEventHandlerWasCalled = false; synchronized (gRecyclerLock) { if (gRecyclerUsed < MAX_RECYCLED) { diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index c16339380294..897e29b3f1e5 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -356,6 +356,16 @@ interface IWindowManager oneway void statusBarVisibilityChanged(int visibility); /** + * Called by System UI to notify of changes to the visibility of Recents. + */ + oneway void setRecentsVisibility(boolean visible); + + /** + * Called by System UI to notify of changes to the visibility of PIP. + */ + oneway void setTvPipVisibility(boolean visible); + + /** * Device has a software navigation bar (separate from the status bar). */ boolean hasNavigationBar(); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index a7337d2581ca..d0708237e304 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -40,7 +40,6 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Insets; import android.graphics.Interpolator; import android.graphics.LinearGradient; @@ -831,6 +830,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, protected static boolean sPreserveMarginParamsInLayoutParamConversion; /** + * Prior to N, when drag enters into child of a view that has already received an + * ACTION_DRAG_ENTERED event, the parent doesn't get a ACTION_DRAG_EXITED event. + * ACTION_DRAG_LOCATION and ACTION_DROP were delivered to the parent of a view that returned + * false from its event handler for these events. + * Starting from N, the parent will get ACTION_DRAG_EXITED event before the child gets its + * ACTION_DRAG_ENTERED. ACTION_DRAG_LOCATION and ACTION_DROP are never propagated to the parent. + * sCascadedDragDrop is true for pre-N apps for backwards compatibility implementation. + */ + static boolean sCascadedDragDrop; + + /** * This view does not want keystrokes. Use with TAKES_FOCUS_MASK when * calling setFlags. */ @@ -3088,20 +3098,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * @hide * - * Whether Recents is visible or not. - */ - public static final int RECENT_APPS_VISIBLE = 0x00004000; - - /** - * @hide - * - * Whether the TV's picture-in-picture is visible or not. - */ - public static final int TV_PICTURE_IN_PICTURE_VISIBLE = 0x00010000; - - /** - * @hide - * * Makes navigation bar transparent (but not the status bar). */ public static final int NAVIGATION_BAR_TRANSPARENT = 0x00008000; @@ -4068,6 +4064,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // in apps so we target check it to avoid breaking existing apps. sPreserveMarginParamsInLayoutParamConversion = targetSdkVersion >= N; + sCascadedDragDrop = targetSdkVersion < N; + sCompatibilityDone = true; } } @@ -20864,6 +20862,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return false; } + // Dispatches ACTION_DRAG_ENTERED and ACTION_DRAG_EXITED events for pre-Nougat apps. + boolean dispatchDragEnterExitInPreN(DragEvent event) { + return callDragEventHandler(event); + } + /** * Detects if this View is enabled and has a drag event listener. * If both are true, then it calls the drag event listener with the @@ -20881,13 +20884,44 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * </p> */ public boolean dispatchDragEvent(DragEvent event) { + event.mEventHandlerWasCalled = true; + if (event.mAction == DragEvent.ACTION_DRAG_LOCATION || + event.mAction == DragEvent.ACTION_DROP) { + // About to deliver an event with coordinates to this view. Notify that now this view + // has drag focus. This will send exit/enter events as needed. + getViewRootImpl().setDragFocus(this, event); + } + return callDragEventHandler(event); + } + + final boolean callDragEventHandler(DragEvent event) { + final boolean result; + ListenerInfo li = mListenerInfo; //noinspection SimplifiableIfStatement if (li != null && li.mOnDragListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnDragListener.onDrag(this, event)) { - return true; + result = true; + } else { + result = onDragEvent(event); } - return onDragEvent(event); + + switch (event.mAction) { + case DragEvent.ACTION_DRAG_ENTERED: { + mPrivateFlags2 |= View.PFLAG2_DRAG_HOVERED; + refreshDrawableState(); + } break; + case DragEvent.ACTION_DRAG_EXITED: { + mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED; + refreshDrawableState(); + } break; + case DragEvent.ACTION_DRAG_ENDED: { + mPrivateFlags2 &= ~View.DRAG_MASK; + refreshDrawableState(); + } break; + } + + return result; } boolean canAcceptDrag() { diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 030fff10d7e1..1b3f6ffe2b64 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -154,8 +154,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ Transformation mInvalidationTransformation; - // View currently under an ongoing drag. Can be null, a child or this window. - private View mCurrentDragView; + // Current frontmost child that can accept drag and lies under the drag location. + // Used only to generate ENTER/EXIT events for pre-Nougat aps. + private View mCurrentDragChild; // Metadata about the ongoing drag private DragEvent mCurrentDragStartEvent; @@ -1359,6 +1360,20 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return mLocalPoint; } + @Override + boolean dispatchDragEnterExitInPreN(DragEvent event) { + if (event.mAction == DragEvent.ACTION_DRAG_EXITED && mCurrentDragChild != null) { + // The drag exited a sub-tree of views; notify of the exit all descendants that are in + // entered state. + // We don't need this recursive delivery for ENTERED events because they get generated + // from the recursive delivery of LOCATION/DROP events, and hence, don't need their own + // recursion. + mCurrentDragChild.dispatchDragEnterExitInPreN(event); + mCurrentDragChild = null; + } + return mIsInterestedInDrag && super.dispatchDragEnterExitInPreN(event); + } + // TODO: Write real docs @Override public boolean dispatchDragEvent(DragEvent event) { @@ -1366,15 +1381,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final float tx = event.mX; final float ty = event.mY; - ViewRootImpl root = getViewRootImpl(); - // Dispatch down the view hierarchy final PointF localPoint = getLocalPoint(); switch (event.mAction) { case DragEvent.ACTION_DRAG_STARTED: { - // clear state to recalculate which views we drag over - mCurrentDragView = null; + // Clear the state to recalculate which views we drag over. + mCurrentDragChild = null; // Set up our tracking of drag-started notifications mCurrentDragStartEvent = DragEvent.obtain(event); @@ -1420,8 +1433,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (child.dispatchDragEvent(event)) { retval = true; } - child.mPrivateFlags2 &= ~View.DRAG_MASK; - child.refreshDrawableState(); } childrenInterestedInDrag.clear(); } @@ -1438,60 +1449,45 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } break; - case DragEvent.ACTION_DRAG_LOCATION: { + case DragEvent.ACTION_DRAG_LOCATION: + case DragEvent.ACTION_DROP: { // Find the [possibly new] drag target View target = findFrontmostDroppableChildAt(event.mX, event.mY, localPoint); - if (target == null && mIsInterestedInDrag) { - target = this; - } - // If we've changed apparent drag target, tell the view root which view - // we're over now [for purposes of the eventual drag-recipient-changed - // notifications to the framework] and tell the new target that the drag - // has entered its bounds. The root will see setDragFocus() calls all - // the way down to the final leaf view that is handling the LOCATION event - // before reporting the new potential recipient to the framework. - if (mCurrentDragView != target) { - root.setDragFocus(target); - - final int action = event.mAction; - // Position should not be available for ACTION_DRAG_ENTERED and ACTION_DRAG_EXITED. - event.mX = 0; - event.mY = 0; - - // If we've dragged off of a child view or this window, send it the EXITED message - if (mCurrentDragView != null) { - final View view = mCurrentDragView; - event.mAction = DragEvent.ACTION_DRAG_EXITED; - if (view != this) { - view.dispatchDragEvent(event); - view.mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED; - view.refreshDrawableState(); - } else { - super.dispatchDragEvent(event); + if (target != mCurrentDragChild) { + if (sCascadedDragDrop) { + // For pre-Nougat apps, make sure that the whole hierarchy of views that contain + // the drag location is kept in the state between ENTERED and EXITED events. + // (Starting with N, only the innermost view will be in that state). + + final int action = event.mAction; + // Position should not be available for ACTION_DRAG_ENTERED and + // ACTION_DRAG_EXITED. + event.mX = 0; + event.mY = 0; + + if (mCurrentDragChild != null) { + event.mAction = DragEvent.ACTION_DRAG_EXITED; + mCurrentDragChild.dispatchDragEnterExitInPreN(event); } - } - - mCurrentDragView = target; - // If we've dragged over a new child view, send it the ENTERED message, otherwise - // send it to this window. - if (target != null) { - event.mAction = DragEvent.ACTION_DRAG_ENTERED; - if (target != this) { - target.dispatchDragEvent(event); - target.mPrivateFlags2 |= View.PFLAG2_DRAG_HOVERED; - target.refreshDrawableState(); - } else { - super.dispatchDragEvent(event); + if (target != null) { + event.mAction = DragEvent.ACTION_DRAG_ENTERED; + target.dispatchDragEnterExitInPreN(event); } + + event.mAction = action; + event.mX = tx; + event.mY = ty; } - event.mAction = action; // restore the event's original state - event.mX = tx; - event.mY = ty; + mCurrentDragChild = target; } - // Dispatch the actual drag location notice, localized into its coordinates + if (target == null && mIsInterestedInDrag) { + target = this; + } + + // Dispatch the actual drag notice, localized into the target coordinates. if (target != null) { if (target != this) { event.mX = localPoint.x; @@ -1501,55 +1497,21 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager event.mX = tx; event.mY = ty; - } else { - retval = super.dispatchDragEvent(event); - } - } - } break; - /* Entered / exited dispatch - * - * DRAG_ENTERED is not dispatched downwards from ViewGroup. The reason for this is - * that we're about to get the corresponding LOCATION event, which we will use to - * determine which of our children is the new target; at that point we will - * push a DRAG_ENTERED down to the new target child [which may itself be a ViewGroup]. - * If no suitable child is detected, dispatch to this window. - * - * DRAG_EXITED *is* dispatched all the way down immediately: once we know the - * drag has left this ViewGroup, we know by definition that every contained subview - * is also no longer under the drag point. - */ + if (mIsInterestedInDrag) { + final boolean eventWasConsumed; + if (sCascadedDragDrop) { + eventWasConsumed = retval; + } else { + eventWasConsumed = event.mEventHandlerWasCalled; + } - case DragEvent.ACTION_DRAG_EXITED: { - if (mCurrentDragView != null) { - final View view = mCurrentDragView; - if (view != this) { - view.dispatchDragEvent(event); - view.mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED; - view.refreshDrawableState(); + if (!eventWasConsumed) { + retval = super.dispatchDragEvent(event); + } + } } else { - super.dispatchDragEvent(event); - } - - mCurrentDragView = null; - } - } break; - - case DragEvent.ACTION_DROP: { - if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "Drop event: " + event); - View target = findFrontmostDroppableChildAt(event.mX, event.mY, localPoint); - if (target != null) { - if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, " dispatch drop to " + target); - event.mX = localPoint.x; - event.mY = localPoint.y; - retval = target.dispatchDragEvent(event); - event.mX = tx; - event.mY = ty; - } else if (mIsInterestedInDrag) { - retval = super.dispatchDragEvent(event); - } else { - if (ViewDebug.DEBUG_DRAG) { - Log.d(View.VIEW_LOG_TAG, " not dropped on an accepting view"); + retval = super.dispatchDragEvent(event); } } } break; @@ -1596,6 +1558,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final boolean canAccept = child.dispatchDragEvent(mCurrentDragStartEvent); mCurrentDragStartEvent.mX = tx; mCurrentDragStartEvent.mY = ty; + mCurrentDragStartEvent.mEventHandlerWasCalled = false; if (canAccept) { mChildrenInterestedInDrag.add(child); if (!child.canAcceptDrag()) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 3bbf75e8e856..ec29abfe4253 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -5527,9 +5527,11 @@ public final class ViewRootImpl implements ViewParent, if (what == DragEvent.ACTION_DRAG_EXITED) { // A direct EXITED event means that the window manager knows we've just crossed // a window boundary, so the current drag target within this one must have - // just been exited. Send it the usual notifications and then we're done - // for now. - mView.dispatchDragEvent(event); + // just been exited. Send the EXITED notification to the current drag view, if any. + if (View.sCascadedDragDrop) { + mView.dispatchDragEnterExitInPreN(event); + } + setDragFocus(null, event); } else { // For events with a [screen] location, translate into window coordinates if ((what == DragEvent.ACTION_DRAG_LOCATION) || (what == DragEvent.ACTION_DROP)) { @@ -5556,6 +5558,12 @@ public final class ViewRootImpl implements ViewParent, // Now dispatch the drag/drop event boolean result = mView.dispatchDragEvent(event); + if (what == DragEvent.ACTION_DRAG_LOCATION && !event.mEventHandlerWasCalled) { + // If the LOCATION event wasn't delivered to any handler, no view now has a drag + // focus. + setDragFocus(null, event); + } + // If we changed apparent drag target, tell the OS about it if (prevDragView != mCurrentDragView) { try { @@ -5582,6 +5590,7 @@ public final class ViewRootImpl implements ViewParent, // When the drag operation ends, reset drag-related state if (what == DragEvent.ACTION_DRAG_ENDED) { + mCurrentDragView = null; setLocalDragState(null); mAttachInfo.mDragToken = null; if (mAttachInfo.mDragSurface != null) { @@ -5641,10 +5650,33 @@ public final class ViewRootImpl implements ViewParent, return mLastTouchSource; } - public void setDragFocus(View newDragTarget) { - if (mCurrentDragView != newDragTarget) { - mCurrentDragView = newDragTarget; + public void setDragFocus(View newDragTarget, DragEvent event) { + if (mCurrentDragView != newDragTarget && !View.sCascadedDragDrop) { + // Send EXITED and ENTERED notifications to the old and new drag focus views. + + final float tx = event.mX; + final float ty = event.mY; + final int action = event.mAction; + // Position should not be available for ACTION_DRAG_ENTERED and ACTION_DRAG_EXITED. + event.mX = 0; + event.mY = 0; + + if (mCurrentDragView != null) { + event.mAction = DragEvent.ACTION_DRAG_EXITED; + mCurrentDragView.callDragEventHandler(event); + } + + if (newDragTarget != null) { + event.mAction = DragEvent.ACTION_DRAG_ENTERED; + newDragTarget.callDragEventHandler(event); + } + + event.mAction = action; + event.mX = tx; + event.mY = ty; } + + mCurrentDragView = newDragTarget; } private AudioManager getAudioManager() { diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 2a524ec489e0..da361c1c04d2 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -1311,6 +1311,16 @@ public interface WindowManagerPolicy { public int adjustSystemUiVisibilityLw(int visibility); /** + * Called by System UI to notify of changes to the visibility of Recents. + */ + public void setRecentsVisibilityLw(boolean visible); + + /** + * Called by System UI to notify of changes to the visibility of PIP. + */ + public void setTvPipVisibilityLw(boolean visible); + + /** * Specifies whether there is an on-screen navigation bar separate from the status bar. */ public boolean hasNavigationBar(); diff --git a/core/java/com/android/server/backup/AccountManagerBackupHelper.java b/core/java/com/android/server/backup/AccountManagerBackupHelper.java new file mode 100644 index 000000000000..39b18c0f2c16 --- /dev/null +++ b/core/java/com/android/server/backup/AccountManagerBackupHelper.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2016 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.server.backup; + +import android.accounts.AccountManagerInternal; +import android.app.backup.BlobBackupHelper; +import android.os.UserHandle; +import android.util.Slog; +import com.android.server.LocalServices; + +/** + * Helper for handling backup of account manager specific state. + */ +public class AccountManagerBackupHelper extends BlobBackupHelper { + private static final String TAG = "AccountsBackup"; + private static final boolean DEBUG = false; + + // current schema of the backup state blob + private static final int STATE_VERSION = 1; + + // key under which the account access grant state blob is committed to backup + private static final String KEY_ACCOUNT_ACCESS_GRANTS = "account_access_grants"; + + public AccountManagerBackupHelper() { + super(STATE_VERSION, KEY_ACCOUNT_ACCESS_GRANTS); + } + + @Override + protected byte[] getBackupPayload(String key) { + AccountManagerInternal am = LocalServices.getService(AccountManagerInternal.class); + if (DEBUG) { + Slog.d(TAG, "Handling backup of " + key); + } + try { + switch (key) { + case KEY_ACCOUNT_ACCESS_GRANTS: { + return am.backupAccountAccessPermissions(UserHandle.USER_SYSTEM); + } + + default: { + Slog.w(TAG, "Unexpected backup key " + key); + } + } + } catch (Exception e) { + Slog.e(TAG, "Unable to store payload " + key); + } + + return new byte[0]; + } + + @Override + protected void applyRestoredPayload(String key, byte[] payload) { + AccountManagerInternal am = LocalServices.getService(AccountManagerInternal.class); + if (DEBUG) { + Slog.d(TAG, "Handling restore of " + key); + } + try { + switch (key) { + case KEY_ACCOUNT_ACCESS_GRANTS: { + am.restoreAccountAccessPermissions(payload, UserHandle.USER_SYSTEM); + } break; + + default: { + Slog.w(TAG, "Unexpected restore key " + key); + } + } + } catch (Exception e) { + Slog.w(TAG, "Unable to restore key " + key); + } + } +} diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java index 9d296fa19400..537565185d9b 100644 --- a/core/java/com/android/server/backup/SystemBackupAgent.java +++ b/core/java/com/android/server/backup/SystemBackupAgent.java @@ -49,6 +49,7 @@ public class SystemBackupAgent extends BackupAgentHelper { private static final String PERMISSION_HELPER = "permissions"; private static final String USAGE_STATS_HELPER = "usage_stats"; private static final String SHORTCUT_MANAGER_HELPER = "shortcut_manager"; + private static final String ACCOUNT_MANAGER_HELPER = "account_manager"; // These paths must match what the WallpaperManagerService uses. The leaf *_FILENAME // are also used in the full-backup file format, so must not change unless steps are @@ -82,6 +83,7 @@ public class SystemBackupAgent extends BackupAgentHelper { addHelper(PERMISSION_HELPER, new PermissionBackupHelper()); addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this)); addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper()); + addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper()); super.onBackup(oldState, data, newState); } @@ -111,6 +113,7 @@ public class SystemBackupAgent extends BackupAgentHelper { addHelper(PERMISSION_HELPER, new PermissionBackupHelper()); addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this)); addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper()); + addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper()); try { super.onRestore(data, appVersionCode, newState); diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp index 9781ae11daec..03462a6d402e 100644 --- a/core/jni/android/graphics/Shader.cpp +++ b/core/jni/android/graphics/Shader.cpp @@ -1,5 +1,6 @@ #include "GraphicsJNI.h" #include "SkGradientShader.h" +#include "SkImagePriv.h" #include "SkShader.h" #include "SkXfermode.h" #include "core_jni_helpers.h" @@ -94,12 +95,15 @@ static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jobject jbitmap, // we'll pass an empty SkBitmap to avoid crashing/excepting for compatibility. GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap); } - SkShader* s = SkShader::CreateBitmapShader(bitmap, - (SkShader::TileMode)tileModeX, - (SkShader::TileMode)tileModeY); - - ThrowIAE_IfNull(env, s); - return reinterpret_cast<jlong>(s); + sk_sp<SkShader> s = SkMakeBitmapShader(bitmap, + (SkShader::TileMode)tileModeX, + (SkShader::TileMode)tileModeY, + nullptr, + kNever_SkCopyPixelsMode, + nullptr); + + ThrowIAE_IfNull(env, s.get()); + return reinterpret_cast<jlong>(s.release()); } /////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp index d453b29c0663..5c879b8894a1 100644 --- a/core/jni/android_os_HwParcel.cpp +++ b/core/jni/android_os_HwParcel.cpp @@ -431,35 +431,6 @@ static void JHwParcel_native_writeString( signalExceptionForError(env, err); } -#define DEFINE_PARCEL_ARRAY_WRITER(Suffix,Type) \ -static void JHwParcel_native_write ## Suffix ## Array( \ - JNIEnv *env, jobject thiz, jint size, Type ## Array valObj) { \ - if (valObj == NULL) { \ - jniThrowException(env, "java/lang/NullPointerException", NULL); \ - return; \ - } \ - \ - jsize len = env->GetArrayLength(valObj); \ - \ - if (len != size) { \ - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); \ - return; \ - } \ - \ - sp<JHwParcel> impl = JHwParcel::GetNativeContext(env, thiz); \ - \ - const Type *val = \ - impl->getStorage()->allocTemporary ## Suffix ## Array(env, valObj); \ - \ - hardware::Parcel *parcel = impl->getParcel(); \ - \ - size_t parentHandle; \ - status_t err = parcel->writeBuffer( \ - val, size * sizeof(*val), &parentHandle); \ - \ - signalExceptionForError(env, err); \ -} - #define DEFINE_PARCEL_VECTOR_WRITER(Suffix,Type) \ static void JHwParcel_native_write ## Suffix ## Vector( \ JNIEnv *env, jobject thiz, Type ## Array valObj) { \ @@ -491,13 +462,6 @@ static void JHwParcel_native_write ## Suffix ## Vector( \ signalExceptionForError(env, err); \ } -DEFINE_PARCEL_ARRAY_WRITER(Int8,jbyte) -DEFINE_PARCEL_ARRAY_WRITER(Int16,jshort) -DEFINE_PARCEL_ARRAY_WRITER(Int32,jint) -DEFINE_PARCEL_ARRAY_WRITER(Int64,jlong) -DEFINE_PARCEL_ARRAY_WRITER(Float,jfloat) -DEFINE_PARCEL_ARRAY_WRITER(Double,jdouble) - DEFINE_PARCEL_VECTOR_WRITER(Int8,jbyte) DEFINE_PARCEL_VECTOR_WRITER(Int16,jshort) DEFINE_PARCEL_VECTOR_WRITER(Int32,jint) @@ -505,43 +469,6 @@ DEFINE_PARCEL_VECTOR_WRITER(Int64,jlong) DEFINE_PARCEL_VECTOR_WRITER(Float,jfloat) DEFINE_PARCEL_VECTOR_WRITER(Double,jdouble) -static void JHwParcel_native_writeBoolArray( - JNIEnv *env, jobject thiz, jint size, jbooleanArray valObj) { - if (valObj == NULL) { - jniThrowException(env, "java/lang/NullPointerException", NULL); - return; - } - - jsize len = env->GetArrayLength(valObj); - - if (len != size) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - return; - } - - sp<JHwParcel> impl = JHwParcel::GetNativeContext(env, thiz); - - jboolean *src = env->GetBooleanArrayElements(valObj, nullptr); - - bool *dst = - (bool *)impl->getStorage()->allocTemporaryStorage(size * sizeof(bool)); - - for (jint i = 0; i < size; ++i) { - dst[i] = src[i]; - } - - env->ReleaseBooleanArrayElements(valObj, src, 0 /* mode */); - src = nullptr; - - hardware::Parcel *parcel = impl->getParcel(); - - size_t parentHandle; - status_t err = parcel->writeBuffer( - dst, size * sizeof(*dst), &parentHandle); - - signalExceptionForError(env, err); -} - static void JHwParcel_native_writeBoolVector( JNIEnv *env, jobject thiz, jbooleanArray valObj) { if (valObj == NULL) { @@ -651,22 +578,6 @@ static jstring JHwParcel_native_readString(JNIEnv *env, jobject thiz) { return MakeStringObjFromHidlString(env, *s); } -#define DEFINE_PARCEL_ARRAY_READER(Suffix,Type,NewType) \ -static Type ## Array JHwParcel_native_read ## Suffix ## Array( \ - JNIEnv *env, jobject thiz, jint size) { \ - hardware::Parcel *parcel = \ - JHwParcel::GetNativeContext(env, thiz)->getParcel(); \ - \ - size_t parentHandle; \ - const Type *val = static_cast<const Type *>( \ - parcel->readBuffer(&parentHandle)); \ - \ - Type ## Array valObj = env->New ## NewType ## Array(size); \ - env->Set ## NewType ## ArrayRegion(valObj, 0, size, val); \ - \ - return valObj; \ -} - #define DEFINE_PARCEL_VECTOR_READER(Suffix,Type,NewType) \ static Type ## Array JHwParcel_native_read ## Suffix ## Vector( \ JNIEnv *env, jobject thiz) { \ @@ -703,13 +614,6 @@ static Type ## Array JHwParcel_native_read ## Suffix ## Vector( \ return valObj; \ } -DEFINE_PARCEL_ARRAY_READER(Int8,jbyte,Byte) -DEFINE_PARCEL_ARRAY_READER(Int16,jshort,Short) -DEFINE_PARCEL_ARRAY_READER(Int32,jint,Int) -DEFINE_PARCEL_ARRAY_READER(Int64,jlong,Long) -DEFINE_PARCEL_ARRAY_READER(Float,jfloat,Float) -DEFINE_PARCEL_ARRAY_READER(Double,jdouble,Double) - DEFINE_PARCEL_VECTOR_READER(Int8,jbyte,Byte) DEFINE_PARCEL_VECTOR_READER(Int16,jshort,Short) DEFINE_PARCEL_VECTOR_READER(Int32,jint,Int) @@ -717,25 +621,6 @@ DEFINE_PARCEL_VECTOR_READER(Int64,jlong,Long) DEFINE_PARCEL_VECTOR_READER(Float,jfloat,Float) DEFINE_PARCEL_VECTOR_READER(Double,jdouble,Double) -static jbooleanArray JHwParcel_native_readBoolArray( - JNIEnv *env, jobject thiz, jint size) { - hardware::Parcel *parcel = - JHwParcel::GetNativeContext(env, thiz)->getParcel(); - - size_t parentHandle; - const bool *val = static_cast<const bool *>( - parcel->readBuffer(&parentHandle)); - - jbooleanArray valObj = env->NewBooleanArray(size); - - for (jint i = 0; i < size; ++i) { - jboolean x = val[i]; - env->SetBooleanArrayRegion(valObj, i, 1, &x); - } - - return valObj; -} - static jbooleanArray JHwParcel_native_readBoolVector( JNIEnv *env, jobject thiz) { hardware::Parcel *parcel = @@ -797,80 +682,6 @@ static jobjectArray MakeStringArray( return arrayObj; } -static jobjectArray JHwParcel_native_readStringArray( - JNIEnv *env, jobject thiz, jint size) { - hardware::Parcel *parcel = - JHwParcel::GetNativeContext(env, thiz)->getParcel(); - - size_t parentHandle; - const hidl_string *val = static_cast<const hidl_string *>( - parcel->readBuffer(&parentHandle)); - - if (val == NULL) { - signalExceptionForError(env, UNKNOWN_ERROR); - return NULL; - } - - status_t err = OK; - for (jint i = 0; (err == OK) && (i < size); ++i) { - err = const_cast<hidl_string *>(&val[i]) - ->readEmbeddedFromParcel( - *parcel, - parentHandle, - i * sizeof(hidl_string)); - } - - if (err != OK) { - signalExceptionForError(env, err); - return NULL; - } - - return MakeStringArray(env, val, size); -} - -static void JHwParcel_native_writeStringArray( - JNIEnv *env, jobject thiz, jint size, jobjectArray arrayObj) { - if (arrayObj == NULL) { - jniThrowException(env, "java/lang/NullPointerException", NULL); - return; - } - - jsize len = env->GetArrayLength(arrayObj); - - if (len != size) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - return; - } - - sp<JHwParcel> impl = JHwParcel::GetNativeContext(env, thiz); - - hidl_string *strings = impl->getStorage()->allocStringArray(len); - - for (jsize i = 0; i < len; ++i) { - ScopedLocalRef<jstring> stringObj( - env, - (jstring)env->GetObjectArrayElement(arrayObj, i)); - - const hidl_string *s = - impl->getStorage()->allocTemporaryString(env, stringObj.get()); - - strings[i].setToExternal(s->c_str(), s->size()); - } - - hardware::Parcel *parcel = impl->getParcel(); - - size_t parentHandle; - status_t err = parcel->writeBuffer( - strings, sizeof(hidl_string) * len, &parentHandle); - - for (jsize i = 0; (err == OK) && (i < len); ++i) { - err = strings[i].writeEmbeddedToParcel( - parcel, parentHandle, i * sizeof(hidl_string)); - } - - signalExceptionForError(env, err); -} - static jobjectArray JHwParcel_native_readStringVector( JNIEnv *env, jobject thiz) { typedef hidl_vec<hidl_string> string_vec; @@ -1047,26 +858,16 @@ static JNINativeMethod gMethods[] = { { "writeString", "(Ljava/lang/String;)V", (void *)JHwParcel_native_writeString }, - { "writeBoolArray", "(I[Z)V", (void *)JHwParcel_native_writeBoolArray }, { "writeBoolVector", "([Z)V", (void *)JHwParcel_native_writeBoolVector }, - { "writeInt8Array", "(I[B)V", (void *)JHwParcel_native_writeInt8Array }, { "writeInt8Vector", "([B)V", (void *)JHwParcel_native_writeInt8Vector }, - { "writeInt16Array", "(I[S)V", (void *)JHwParcel_native_writeInt16Array }, { "writeInt16Vector", "([S)V", (void *)JHwParcel_native_writeInt16Vector }, - { "writeInt32Array", "(I[I)V", (void *)JHwParcel_native_writeInt32Array }, { "writeInt32Vector", "([I)V", (void *)JHwParcel_native_writeInt32Vector }, - { "writeInt64Array", "(I[J)V", (void *)JHwParcel_native_writeInt64Array }, { "writeInt64Vector", "([J)V", (void *)JHwParcel_native_writeInt64Vector }, - { "writeFloatArray", "(I[F)V", (void *)JHwParcel_native_writeFloatArray }, { "writeFloatVector", "([F)V", (void *)JHwParcel_native_writeFloatVector }, - { "writeDoubleArray", "(I[D)V", (void *)JHwParcel_native_writeDoubleArray }, { "writeDoubleVector", "([D)V", (void *)JHwParcel_native_writeDoubleVector }, - { "writeStringArray", "(I[Ljava/lang/String;)V", - (void *)JHwParcel_native_writeStringArray }, - { "writeStringVector", "([Ljava/lang/String;)V", (void *)JHwParcel_native_writeStringVector }, @@ -1087,24 +888,14 @@ static JNINativeMethod gMethods[] = { { "readString", "()Ljava/lang/String;", (void *)JHwParcel_native_readString }, - { "readBoolArray", "(I)[Z", (void *)JHwParcel_native_readBoolArray }, { "readBoolVector", "()[Z", (void *)JHwParcel_native_readBoolVector }, - { "readInt8Array", "(I)[B", (void *)JHwParcel_native_readInt8Array }, { "readInt8Vector", "()[B", (void *)JHwParcel_native_readInt8Vector }, - { "readInt16Array", "(I)[S", (void *)JHwParcel_native_readInt16Array }, { "readInt16Vector", "()[S", (void *)JHwParcel_native_readInt16Vector }, - { "readInt32Array", "(I)[I", (void *)JHwParcel_native_readInt32Array }, { "readInt32Vector", "()[I", (void *)JHwParcel_native_readInt32Vector }, - { "readInt64Array", "(I)[J", (void *)JHwParcel_native_readInt64Array }, { "readInt64Vector", "()[J", (void *)JHwParcel_native_readInt64Vector }, - { "readFloatArray", "(I)[F", (void *)JHwParcel_native_readFloatArray }, { "readFloatVector", "()[F", (void *)JHwParcel_native_readFloatVector }, - { "readDoubleArray", "(I)[D", (void *)JHwParcel_native_readDoubleArray }, { "readDoubleVector", "()[D", (void *)JHwParcel_native_readDoubleVector }, - { "readStringArray", "(I)[Ljava/lang/String;", - (void *)JHwParcel_native_readStringArray }, - { "readStringVector", "()[Ljava/lang/String;", (void *)JHwParcel_native_readStringVector }, diff --git a/core/jni/hwbinder/EphemeralStorage.cpp b/core/jni/hwbinder/EphemeralStorage.cpp index 187beeea4de1..4996bc86cade 100644 --- a/core/jni/hwbinder/EphemeralStorage.cpp +++ b/core/jni/hwbinder/EphemeralStorage.cpp @@ -71,21 +71,6 @@ const hidl_string *EphemeralStorage::allocTemporaryString( return s; } -#define DEFINE_ALLOC_ARRAY_METHODS(Suffix,Type,NewType) \ -const Type *EphemeralStorage::allocTemporary ## Suffix ## Array( \ - JNIEnv *env, Type ## Array arrayObj) { \ - Type ## Array obj = (Type ## Array)env->NewGlobalRef(arrayObj); \ - const Type *val = env->Get ## NewType ## ArrayElements(obj, NULL); \ - \ - Item item; \ - item.mType = TYPE_ ## Suffix ## _ARRAY; \ - item.mObj = obj; \ - item.mPtr = (void *)val; \ - mItems.push_back(item); \ - \ - return val; \ -} - #define DEFINE_ALLOC_VECTOR_METHODS(Suffix,Type,NewType) \ const hidl_vec<Type> *EphemeralStorage::allocTemporary ## Suffix ## Vector( \ JNIEnv *env, Type ## Array arrayObj) { \ @@ -107,13 +92,6 @@ const hidl_vec<Type> *EphemeralStorage::allocTemporary ## Suffix ## Vector( \ return vec; \ } -DEFINE_ALLOC_ARRAY_METHODS(Int8,jbyte,Byte) -DEFINE_ALLOC_ARRAY_METHODS(Int16,jshort,Short) -DEFINE_ALLOC_ARRAY_METHODS(Int32,jint,Int) -DEFINE_ALLOC_ARRAY_METHODS(Int64,jlong,Long) -DEFINE_ALLOC_ARRAY_METHODS(Float,jfloat,Float) -DEFINE_ALLOC_ARRAY_METHODS(Double,jdouble,Double) - DEFINE_ALLOC_VECTOR_METHODS(Int8,jbyte,Byte) DEFINE_ALLOC_VECTOR_METHODS(Int16,jshort,Short) DEFINE_ALLOC_VECTOR_METHODS(Int32,jint,Int) diff --git a/core/jni/hwbinder/EphemeralStorage.h b/core/jni/hwbinder/EphemeralStorage.h index b02ed9f5ea69..f07c782bfdf7 100644 --- a/core/jni/hwbinder/EphemeralStorage.h +++ b/core/jni/hwbinder/EphemeralStorage.h @@ -26,9 +26,6 @@ namespace android { #define DECLARE_ALLOC_METHODS(Suffix,Type) \ - const Type *allocTemporary ## Suffix ## Array( \ - JNIEnv *env, Type ## Array arrayObj); \ - \ const ::android::hardware::hidl_vec<Type> * \ allocTemporary ## Suffix ## Vector( \ JNIEnv *env, Type ## Array arrayObj); diff --git a/docs/html/_redirects.yaml b/docs/html/_redirects.yaml index 7e7c2de04b68..3458bdc73149 100644 --- a/docs/html/_redirects.yaml +++ b/docs/html/_redirects.yaml @@ -1282,3 +1282,6 @@ redirects: - from: /preview/features/data-saver.html to: /training/basics/network-ops/data-saver.html +# Temporary redirect +- from: /ndk/guides/cmake.html + to: https://sites.google.com/a/android.com/tools/tech-docs/external-c-builds#TOC-CMake-Variables-List
\ No newline at end of file diff --git a/docs/html/develop/index.jd b/docs/html/develop/index.jd index cee8327a3008..b1d738832b3a 100644 --- a/docs/html/develop/index.jd +++ b/docs/html/develop/index.jd @@ -27,7 +27,7 @@ rewritten <b>layout editor</b> with the new constraint layout, helping you build rich UI with less work.</p> -<p class="dac-hero-description">With over a dozen new features, Android Studio +<p class="dac-hero-description">With over 20 new features, Android Studio 2.2 helps you code faster and smarter.</p> <p style="margin-top:24px"> diff --git a/docs/html/index.jd b/docs/html/index.jd index 32360fd0e932..39e3d73439ee 100644 --- a/docs/html/index.jd +++ b/docs/html/index.jd @@ -75,7 +75,7 @@ nonavpage=true <div class="col-7of16 col-pull-6of16"> <h1 class="dac-hero-title" style="color:#004d40">Android Studio 2.2!</h1> <p class="dac-hero-description" style="color:#004d40">The latest update is -packed with over a dozen new features, like a rewritten layout editor with the +packed with over 20 new features, like a rewritten layout editor with the new constraint layout, support for Android 7.0 Nougat, Espresso test recording, enhanced Jack compiler / Java 8 support, expanded C++ support with CMake and NDK-Build, and much more!</p> diff --git a/docs/html/jd_extras_en.js b/docs/html/jd_extras_en.js index f3469b4f4ec1..e4bd36856c9f 100644 --- a/docs/html/jd_extras_en.js +++ b/docs/html/jd_extras_en.js @@ -4127,8 +4127,8 @@ METADATA['en'].collections = { "develop/landing/tools": { "title": "", "resources": [ - "https://www.youtube.com/watch?v=ZOz_yr8Yxq8&list=PLWz5rJ2EKKc_w6fodMGrA1_tsI3pqPbqa", - "https://www.youtube.com/watch?v=eOV2owswDkE&list=PLWz5rJ2EKKc_w6fodMGrA1_tsI3pqPbqa", + "https://www.youtube.com/watch?v=NbHsi3-uR8E&list=PLWz5rJ2EKKc_w6fodMGrA1_tsI3pqPbqa", + "https://www.youtube.com/watch?v=-SY5nkNVUn0&list=PLWz5rJ2EKKc_w6fodMGrA1_tsI3pqPbqa", "https://www.youtube.com/watch?v=StqAZ1OQbqA&list=PLWz5rJ2EKKc_w6fodMGrA1_tsI3pqPbqa", "https://www.youtube.com/watch?v=-SY5nkNVUn0&list=PLWz5rJ2EKKc_w6fodMGrA1_tsI3pqPbqa", "https://www.youtube.com/watch?v=4rI4tTd7-J8&list=PLWz5rJ2EKKc_w6fodMGrA1_tsI3pqPbqa", @@ -5568,7 +5568,7 @@ METADATA['en'].collections = { "title": "", "resources": [ "https://medium.com/google-developers/how-often-should-you-update-android-studio-db25785c488e#.8blbql35x", - "http://android-developers.blogspot.com/2016/04/android-studio-2-0.html", + "https://android-developers.blogspot.com/2016/09/android-studio-2-2.html", "https://medium.com/google-developers/writing-more-code-by-writing-less-code-with-android-studio-live-templates-244f648d17c7#.hczcm02du", ] }, diff --git a/docs/html/topic/arc/index.jd b/docs/html/topic/arc/index.jd index d46fbc89ac59..a025459226aa 100644 --- a/docs/html/topic/arc/index.jd +++ b/docs/html/topic/arc/index.jd @@ -51,7 +51,7 @@ review your mouse and keyboard interactions. <!-- Some Chromebooks don't support touch. Although not essential, it's a good idea to explicitly include this declaration. --> <uses-feature android:name="android.hardware.touchscreen" - required="false" /> + android:required="false" /> </manifest> </pre> diff --git a/docs/html/training/testing/unit-testing/instrumented-unit-tests.jd b/docs/html/training/testing/unit-testing/instrumented-unit-tests.jd index df8b1bc5c9a6..dc94bdf05502 100644 --- a/docs/html/training/testing/unit-testing/instrumented-unit-tests.jd +++ b/docs/html/training/testing/unit-testing/instrumented-unit-tests.jd @@ -53,8 +53,9 @@ you choose, to simulate any dependency relationships.</p> <p>In your Android Studio project, you must store the source files for instrumented tests at -<code><var>module-name</var>/src/androidTests/java/</code>. This directory -already exists when you create a new project.</p> +<code><var>module-name</var>/src/androidTest/java/</code>. This directory +already exists when you create a new project and contains an example +instrumented test.</p> <p>Before you begin, you should <a href="{@docRoot}tools/testing-support-library/index.html#setup">download diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index a3c9a3944159..9553ab459b5c 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -26,6 +26,7 @@ #include <SkDrawFilter.h> #include <SkGraphics.h> #include <SkImage.h> +#include <SkImagePriv.h> #include <SkRSXform.h> #include <SkShader.h> #include <SkTemplates.h> @@ -594,10 +595,13 @@ void SkiaCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshH if (paint) { tmpPaint = *paint; } - SkShader* shader = SkShader::CreateBitmapShader(bitmap, - SkShader::kClamp_TileMode, - SkShader::kClamp_TileMode); - SkSafeUnref(tmpPaint.setShader(shader)); + sk_sp<SkShader> shader = SkMakeBitmapShader(bitmap, + SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode, + nullptr, + kNever_SkCopyPixelsMode, + nullptr); + tmpPaint.setShader(std::move(shader)); mCanvas->drawVertices(SkCanvas::kTriangles_VertexMode, ptCount, (SkPoint*)vertices, texs, (const SkColor*)colors, NULL, indices, diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp index fdf0b547fb54..edc7191459b7 100644 --- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp +++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp @@ -740,10 +740,13 @@ TEST(RecordingCanvas, refBitmapInShader_bitmapShader) { SkBitmap bitmap = TestUtils::createSkBitmap(100, 100); auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) { SkPaint paint; - SkAutoTUnref<SkShader> shader(SkShader::CreateBitmapShader(bitmap, + sk_sp<SkShader> shader = SkMakeBitmapShader(bitmap, SkShader::TileMode::kClamp_TileMode, - SkShader::TileMode::kClamp_TileMode)); - paint.setShader(shader); + SkShader::TileMode::kClamp_TileMode, + nullptr, + kNever_SkCopyPixelsMode, + nullptr); + paint.setShader(std::move(shader)); canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint); }); auto& bitmaps = dl->getBitmapResources(); @@ -754,21 +757,24 @@ TEST(RecordingCanvas, refBitmapInShader_composeShader) { SkBitmap bitmap = TestUtils::createSkBitmap(100, 100); auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) { SkPaint paint; - SkAutoTUnref<SkShader> shader1(SkShader::CreateBitmapShader(bitmap, + sk_sp<SkShader> shader1 = SkMakeBitmapShader(bitmap, + SkShader::TileMode::kClamp_TileMode, SkShader::TileMode::kClamp_TileMode, - SkShader::TileMode::kClamp_TileMode)); + nullptr, + kNever_SkCopyPixelsMode, + nullptr); SkPoint center; center.set(50, 50); SkColor colors[2]; colors[0] = Color::Black; colors[1] = Color::White; - SkAutoTUnref<SkShader> shader2(SkGradientShader::CreateRadial(center, 50, colors, nullptr, 2, - SkShader::TileMode::kRepeat_TileMode)); + sk_sp<SkShader> shader2 = SkGradientShader::MakeRadial(center, 50, colors, nullptr, 2, + SkShader::TileMode::kRepeat_TileMode); - SkAutoTUnref<SkShader> composeShader(SkShader::CreateComposeShader(shader1, shader2, - SkXfermode::Mode::kMultiply_Mode)); - paint.setShader(composeShader); + sk_sp<SkShader> composeShader = SkShader::MakeComposeShader(std::move(shader1), std::move(shader2), + SkXfermode::Mode::kMultiply_Mode); + paint.setShader(std::move(composeShader)); canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint); }); auto& bitmaps = dl->getBitmapResources(); diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp index 158938a85907..a30ada0df453 100644 --- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp +++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp @@ -17,8 +17,9 @@ #include "tests/common/TestUtils.h" #include <gtest/gtest.h> -#include <SkShader.h> #include <SkColorMatrixFilter.h> +#include <SkImagePriv.h> +#include <SkShader.h> using namespace android; using namespace android::uirenderer; @@ -29,10 +30,13 @@ using namespace android::uirenderer; */ TEST(SkiaBehavior, CreateBitmapShader1x1) { SkBitmap origBitmap = TestUtils::createSkBitmap(1, 1); - SkAutoTUnref<SkShader> s(SkShader::CreateBitmapShader( + sk_sp<SkShader> s = SkMakeBitmapShader( origBitmap, SkShader::kClamp_TileMode, - SkShader::kRepeat_TileMode)); + SkShader::kRepeat_TileMode, + nullptr, + kNever_SkCopyPixelsMode, + nullptr); SkBitmap bitmap; SkShader::TileMode xy[2]; diff --git a/packages/SystemUI/res/drawable/ic_qs_data_disabled.xml b/packages/SystemUI/res/drawable/ic_qs_data_disabled.xml index b4144a3474fd..d11b6f461870 100644 --- a/packages/SystemUI/res/drawable/ic_qs_data_disabled.xml +++ b/packages/SystemUI/res/drawable/ic_qs_data_disabled.xml @@ -14,8 +14,9 @@ limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24.0dp" - android:height="24.0dp" + android:autoMirrored="true" + android:width="32.0dp" + android:height="32.0dp" android:viewportWidth="40.0" android:viewportHeight="40.0"> <path diff --git a/packages/SystemUI/res/drawable/stat_sys_data_disabled.xml b/packages/SystemUI/res/drawable/stat_sys_data_disabled.xml index 4e2a0245ab2a..694019e98867 100644 --- a/packages/SystemUI/res/drawable/stat_sys_data_disabled.xml +++ b/packages/SystemUI/res/drawable/stat_sys_data_disabled.xml @@ -14,9 +14,9 @@ limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="17.0dp" + android:width="8.5dp" android:height="17.0dp" - android:viewportWidth="40.0" + android:viewportWidth="20.0" android:viewportHeight="40.0"> <path android:fillColor="#FFFFFFFF" diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index 610418171266..d483e4240315 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -160,6 +160,10 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha for (QSTile<?> tile : tiles) { QSTileBaseView tileView = mQsPanel.getTileView(tile); + if (tileView == null) { + Log.e(TAG, "tileView is null " + tile.getTileSpec()); + continue; + } final TextView label = ((QSTileView) tileView).getLabel(); final View tileIcon = tileView.getIcon().getIconView(); if (count < mNumQuickTiles && mAllowFancy) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 784d2ba34c48..e5493b645078 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -17,6 +17,7 @@ package com.android.systemui.recents; import android.app.Activity; +import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.TaskStackBuilder; import android.content.BroadcastReceiver; @@ -89,6 +90,7 @@ import com.android.systemui.statusbar.BaseStatusBar; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.List; /** * The main Recents activity that is started from RecentsComponent. @@ -167,18 +169,39 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD */ final BroadcastReceiver mSystemBroadcastReceiver = new BroadcastReceiver() { @Override - public void onReceive(Context context, Intent intent) { + public void onReceive(Context ctx, Intent intent) { String action = intent.getAction(); if (action.equals(Intent.ACTION_SCREEN_OFF)) { // When the screen turns off, dismiss Recents to Home dismissRecentsToHomeIfVisible(false); } else if (action.equals(Intent.ACTION_TIME_CHANGED)) { - // For the time being, if the time changes, then invalidate the - // last-stack-active-time, this ensures that we will just show the last N tasks - // the next time that Recents loads, but prevents really old tasks from showing - // up if the task time is set forward. - Prefs.putLong(RecentsActivity.this, Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, - 0); + // If the time shifts but the currentTime >= lastStackActiveTime, then that boundary + // is still valid. Otherwise, we need to reset the lastStackactiveTime to the + // currentTime and remove the old tasks in between which would not be previously + // visible, but currently would be in the new currentTime + long oldLastStackActiveTime = Prefs.getLong(RecentsActivity.this, + Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, -1); + if (oldLastStackActiveTime != -1) { + long currentTime = System.currentTimeMillis(); + if (currentTime < oldLastStackActiveTime) { + // We are only removing tasks that are between the new current time + // and the old last stack active time, they were not visible and in the + // TaskStack so we don't need to remove any associated TaskViews but we do + // need to load the task id's from the system + RecentsTaskLoadPlan loadPlan = Recents.getTaskLoader().createLoadPlan(ctx); + loadPlan.preloadRawTasks(false /* includeFrontMostExcludedTask */); + List<ActivityManager.RecentTaskInfo> tasks = loadPlan.getRawTasks(); + for (int i = tasks.size() - 1; i >= 0; i--) { + ActivityManager.RecentTaskInfo task = tasks.get(i); + if (currentTime <= task.lastActiveTime && task.lastActiveTime < + oldLastStackActiveTime) { + Recents.getSystemServices().removeTask(task.persistentId); + } + } + Prefs.putLong(RecentsActivity.this, + Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, currentTime); + } + } } } }; diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 83f98e5d874c..44a5bd3407b2 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -217,11 +217,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener * {@link Recents#onBusEvent(RecentsVisibilityChangedEvent)}. */ public void onVisibilityChanged(Context context, boolean visible) { - SystemUIApplication app = (SystemUIApplication) context; - PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class); - if (statusBar != null) { - statusBar.updateRecentsVisibility(visible); - } + Recents.getSystemServices().setRecentsVisibility(visible); } /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index 9d9e2738973e..e0cdb1ac7c62 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -70,6 +70,7 @@ import android.util.MutableBoolean; import android.view.Display; import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IDockedStackListener; +import android.view.IWindowManager; import android.view.WindowManager; import android.view.WindowManager.KeyboardShortcutsReceiver; import android.view.WindowManagerGlobal; @@ -121,6 +122,7 @@ public class SystemServicesProxy { IPackageManager mIpm; AssistUtils mAssistUtils; WindowManager mWm; + IWindowManager mIwm; UserManager mUm; Display mDisplay; String mRecentsPackage; @@ -208,6 +210,7 @@ public class SystemServicesProxy { mIpm = AppGlobals.getPackageManager(); mAssistUtils = new AssistUtils(context); mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + mIwm = WindowManagerGlobal.getWindowManagerService(); mUm = UserManager.get(context); mDisplay = mWm.getDefaultDisplay(); mRecentsPackage = context.getPackageName(); @@ -1097,6 +1100,28 @@ public class SystemServicesProxy { } } + /** + * Updates the visibility of recents. + */ + public void setRecentsVisibility(boolean visible) { + try { + mIwm.setRecentsVisibility(visible); + } catch (RemoteException e) { + Log.e(TAG, "Unable to reach window manager", e); + } + } + + /** + * Updates the visibility of the picture-in-picture. + */ + public void setTvPipVisibility(boolean visible) { + try { + mIwm.setTvPipVisibility(visible); + } catch (RemoteException e) { + Log.e(TAG, "Unable to reach window manager", e); + } + } + private final class H extends Handler { private static final int ON_TASK_STACK_CHANGED = 1; private static final int ON_ACTIVITY_PINNED = 2; diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java index 1278b735a7cd..9b48e4d02623 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java @@ -248,6 +248,13 @@ public class RecentsTaskLoadPlan { return mStack; } + /** + * Returns the raw list of recent tasks. + */ + public List<ActivityManager.RecentTaskInfo> getRawTasks() { + return mRawTasks; + } + /** Returns whether there are any tasks in any stacks. */ public boolean hasTasks() { if (mStack != null) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java index fca8d2d1fae1..ef9de53682e4 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java @@ -140,10 +140,6 @@ public class RecentsTvImpl extends RecentsImpl{ @Override public void onVisibilityChanged(Context context, boolean visible) { - SystemUIApplication app = (SystemUIApplication) context; - TvStatusBar statusBar = app.getComponent(TvStatusBar.class); - if (statusBar != null) { - statusBar.updateRecentsVisibility(visible); - } + Recents.getSystemServices().setRecentsVisibility(visible); } } 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 4c40da0ef2fc..089f7a9f0503 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -3003,10 +3003,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, Integer.toHexString(diff))); boolean sbModeChanged = false; if (diff != 0) { - // we never set the recents bit via this method, so save the prior state to prevent - // clobbering the bit below - final boolean wasRecentsVisible = (mSystemUiVisibility & View.RECENT_APPS_VISIBLE) > 0; - mSystemUiVisibility = newVal; // update low profile @@ -3055,11 +3051,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE; } - // restore the recents bit - if (wasRecentsVisible) { - mSystemUiVisibility |= View.RECENT_APPS_VISIBLE; - } - // send updated sysui visibility to window manager notifyUiVisibilityChanged(mSystemUiVisibility); } @@ -4848,16 +4839,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, return false; } - public void updateRecentsVisibility(boolean visible) { - // Update the recents visibility flag - if (visible) { - mSystemUiVisibility |= View.RECENT_APPS_VISIBLE; - } else { - mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE; - } - notifyUiVisibilityChanged(mSystemUiVisibility); - } - @Override public void showScreenPinningRequest(int taskId) { if (mKeyguardMonitor.isShowing()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java index 2d4900b36890..3c83921a1e14 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -19,7 +19,6 @@ package com.android.systemui.statusbar.tv; import android.content.ComponentName; import android.graphics.Rect; import android.os.IBinder; -import android.os.RemoteException; import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; import android.view.View; @@ -36,16 +35,6 @@ import com.android.systemui.tv.pip.PipManager; public class TvStatusBar extends BaseStatusBar { - /** - * Tracking calls to View.setSystemUiVisibility(). - */ - int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE; - - /** - * Last value sent to window manager. - */ - private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE; - @Override public void setIcon(String slot, StatusBarIcon icon) { } @@ -224,40 +213,6 @@ public class TvStatusBar extends BaseStatusBar { putComponent(TvStatusBar.class, this); } - /** - * Updates the visibility of the picture-in-picture. - */ - public void updatePipVisibility(boolean visible) { - if (visible) { - mSystemUiVisibility |= View.TV_PICTURE_IN_PICTURE_VISIBLE; - } else { - mSystemUiVisibility &= ~View.TV_PICTURE_IN_PICTURE_VISIBLE; - } - notifyUiVisibilityChanged(mSystemUiVisibility); - } - - /** - * Updates the visibility of the Recents - */ - public void updateRecentsVisibility(boolean visible) { - if (visible) { - mSystemUiVisibility |= View.RECENT_APPS_VISIBLE; - } else { - mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE; - } - notifyUiVisibilityChanged(mSystemUiVisibility); - } - - private void notifyUiVisibilityChanged(int vis) { - try { - if (mLastDispatchedSystemUiVisibility != vis) { - mWindowManagerService.statusBarVisibilityChanged(vis); - mLastDispatchedSystemUiVisibility = vis; - } - } catch (RemoteException ex) { - } - } - @Override public void handleSystemNavigationKey(int arg1) { // Not implemented diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java index 3f8650a5e116..085e003f8869 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java @@ -723,10 +723,7 @@ public class PipManager { return mPipRecentsOverlayManager; } - private void updatePipVisibility(boolean visible) { - TvStatusBar statusBar = ((SystemUIApplication) mContext).getComponent(TvStatusBar.class); - if (statusBar != null) { - statusBar.updatePipVisibility(visible); - } + private void updatePipVisibility(final boolean visible) { + SystemServicesProxy.getInstance(mContext).setTvPipVisibility(visible); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java new file mode 100644 index 000000000000..8eecfcf00d46 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2016 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.qs; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyBoolean; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.SmallTest; +import com.android.systemui.R; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class TileLayoutTest { + private Context mContext = InstrumentationRegistry.getTargetContext(); + private final TileLayout mTileLayout = new TileLayout(mContext); + private int mLayoutSizeForOneTile; + + @Before + public void setUp() throws Exception { + // Layout needs to leave space for the tile margins. Three times the margin size is + // sufficient for any number of columns. + mLayoutSizeForOneTile = + mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_margin) * 3; + } + + private QSPanel.TileRecord createTileRecord() { + QSPanel.TileRecord tileRecord = new QSPanel.TileRecord(); + tileRecord.tile = mock(QSTile.class); + tileRecord.tileView = spy(new QSTileBaseView(mContext, new QSIconView(mContext))); + return tileRecord; + } + + @Test + public void testAddTile_CallsSetListeningOnTile() { + QSPanel.TileRecord tileRecord = createTileRecord(); + mTileLayout.addTile(tileRecord); + verify(tileRecord.tile, times(1)).setListening(mTileLayout, false); + } + + @Test + public void testSetListening_CallsSetListeningOnTile() { + QSPanel.TileRecord tileRecord = createTileRecord(); + mTileLayout.addTile(tileRecord); + mTileLayout.setListening(true); + verify(tileRecord.tile, times(1)).setListening(mTileLayout, true); + } + + @Test + public void testSetListening_SameValueIsNoOp() { + QSPanel.TileRecord tileRecord = createTileRecord(); + mTileLayout.addTile(tileRecord); + mTileLayout.setListening(false); + verify(tileRecord.tile, times(1)).setListening(any(), anyBoolean()); + } + + @Test + public void testSetListening_ChangesValueForAddingFutureTiles() { + QSPanel.TileRecord tileRecord = createTileRecord(); + mTileLayout.setListening(true); + mTileLayout.addTile(tileRecord); + verify(tileRecord.tile, times(1)).setListening(mTileLayout, true); + } + + @Test + public void testRemoveTile_CallsSetListeningFalseOnTile() { + QSPanel.TileRecord tileRecord = createTileRecord(); + mTileLayout.setListening(true); + mTileLayout.addTile(tileRecord); + mTileLayout.removeTile(tileRecord); + verify(tileRecord.tile, times(1)).setListening(mTileLayout, false); + } + + @Test + public void testRemoveAllViews_CallsSetListeningFalseOnAllTiles() { + QSPanel.TileRecord tileRecord1 = createTileRecord(); + QSPanel.TileRecord tileRecord2 = createTileRecord(); + mTileLayout.setListening(true); + mTileLayout.addTile(tileRecord1); + mTileLayout.addTile(tileRecord2); + mTileLayout.removeAllViews(); + verify(tileRecord1.tile, times(1)).setListening(mTileLayout, false); + verify(tileRecord2.tile, times(1)).setListening(mTileLayout, false); + } + + @Test + public void testMeasureLayout_CallsLayoutOnTile() { + QSPanel.TileRecord tileRecord = createTileRecord(); + mTileLayout.addTile(tileRecord); + mTileLayout.measure(mLayoutSizeForOneTile, mLayoutSizeForOneTile); + mTileLayout.layout(0, 0, mLayoutSizeForOneTile, mLayoutSizeForOneTile); + verify(tileRecord.tileView, times(1)).layout(anyInt(), anyInt(), anyInt(), anyInt()); + } + + @Test + public void testMeasureLayout_CallsLayoutOnTilesWithNeighboredBounds() { + QSPanel.TileRecord tileRecord1 = createTileRecord(); + QSPanel.TileRecord tileRecord2 = createTileRecord(); + mTileLayout.addTile(tileRecord1); + mTileLayout.addTile(tileRecord2); + mTileLayout.measure(mLayoutSizeForOneTile * 2, mLayoutSizeForOneTile * 2); + mTileLayout.layout(0, 0, mLayoutSizeForOneTile * 2, mLayoutSizeForOneTile * 2); + + // Capture the layout calls for both tiles. + ArgumentCaptor<Integer> left1 = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> top1 = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> right1 = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> bottom1 = ArgumentCaptor.forClass(Integer.class); + verify(tileRecord1.tileView, times(1)) + .layout(left1.capture(), top1.capture(), right1.capture(), bottom1.capture()); + ArgumentCaptor<Integer> left2 = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> top2 = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> right2 = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> bottom2 = ArgumentCaptor.forClass(Integer.class); + verify(tileRecord2.tileView, times(1)) + .layout(left2.capture(), top2.capture(), right2.capture(), bottom2.capture()); + + // We assume two tiles will always fit side-by-side. + assertTrue(mContext.getResources().getInteger(R.integer.quick_settings_num_columns) > 1); + + // left <= right, top <= bottom + assertTrue(left1.getValue() <= right1.getValue()); + assertTrue(top1.getValue() <= bottom1.getValue()); + assertTrue(left2.getValue() <= right2.getValue()); + assertTrue(top2.getValue() <= bottom2.getValue()); + + // The tiles' left and right should describe discrete ranges. + // Agnostic of Layout direction. + assertTrue(left1.getValue() > right2.getValue() || right1.getValue() < left2.getValue()); + + // The tiles' Top and Bottom should be the same. + assertEquals(top1.getValue().intValue(), top2.getValue().intValue()); + assertEquals(bottom1.getValue().intValue(), bottom2.getValue().intValue()); + } +} diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java index 3a82f8828b6d..ff934ef18677 100644 --- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java +++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java @@ -147,11 +147,12 @@ public class WallpaperBackupAgent extends BackupAgent { } // only back up the wallpapers if we've been told they're eligible - if ((sysEligible || lockEligible) && mWallpaperInfo.exists()) { + if (mWallpaperInfo.exists()) { if (sysChanged || lockChanged || !infoStage.exists()) { if (DEBUG) Slog.v(TAG, "New wallpaper configuration; copying"); FileUtils.copyFileOrThrow(mWallpaperInfo, infoStage); } + if (DEBUG) Slog.v(TAG, "Storing wallpaper metadata"); fullBackupFile(infoStage, data); } if (sysEligible && mWallpaperFile.exists()) { @@ -159,6 +160,7 @@ public class WallpaperBackupAgent extends BackupAgent { if (DEBUG) Slog.v(TAG, "New system wallpaper; copying"); FileUtils.copyFileOrThrow(mWallpaperFile, imageStage); } + if (DEBUG) Slog.v(TAG, "Storing system wallpaper image"); fullBackupFile(imageStage, data); prefs.edit().putInt(SYSTEM_GENERATION, sysGeneration).apply(); } @@ -169,6 +171,7 @@ public class WallpaperBackupAgent extends BackupAgent { if (DEBUG) Slog.v(TAG, "New lock wallpaper; copying"); FileUtils.copyFileOrThrow(mLockWallpaperFile, lockImageStage); } + if (DEBUG) Slog.v(TAG, "Storing lock wallpaper image"); fullBackupFile(lockImageStage, data); prefs.edit().putInt(LOCK_GENERATION, lockGeneration).apply(); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java index 562d95065cd8..582b19b62c22 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java @@ -341,6 +341,8 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen mDoubleTapDetected = false; mSecondFingerDoubleTap = false; mGestureStarted = false; + mGestureDetector.onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_CANCEL, + 0.0f, 0.0f, 0)); cancelGesture(); } diff --git a/services/core/Android.mk b/services/core/Android.mk index b965ce39e7d5..a5b1069974f2 100644 --- a/services/core/Android.mk +++ b/services/core/Android.mk @@ -11,7 +11,7 @@ LOCAL_SRC_FILES += \ java/com/android/server/EventLogTags.logtags \ java/com/android/server/am/EventLogTags.logtags \ ../../../../system/netd/server/binder/android/net/INetd.aidl \ - ../../../../system/netd/server/binder/android/net/metrics/IDnsEventListener.aidl \ + ../../../../system/netd/server/binder/android/net/metrics/INetdEventListener.aidl \ LOCAL_AIDL_INCLUDES += \ system/netd/server/binder diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 667a4a9e1b47..4111d21e6112 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -372,6 +372,11 @@ public class ConnectivityService extends IConnectivityManager.Stub private static final int EVENT_SET_ACCEPT_UNVALIDATED = 28; /** + * used to specify whether a network should not be penalized when it becomes unvalidated. + */ + private static final int EVENT_SET_AVOID_UNVALIDATED = 35; + + /** * used to ask the user to confirm a connection to an unvalidated network. * obj = network */ @@ -2064,7 +2069,9 @@ public class ConnectivityService extends IConnectivityManager.Stub mKeepaliveTracker.dump(pw); pw.println(); + dumpAvoidBadWifiSettings(pw); + pw.println(); if (mInetLog != null && mInetLog.size() > 0) { pw.println(); pw.println("Inet condition reports:"); @@ -2720,6 +2727,12 @@ public class ConnectivityService extends IConnectivityManager.Stub accept ? 1 : 0, always ? 1: 0, network)); } + @Override + public void setAvoidUnvalidated(Network network) { + enforceConnectivityInternalPermission(); + mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_AVOID_UNVALIDATED, network)); + } + private void handleSetAcceptUnvalidated(Network network, boolean accept, boolean always) { if (DBG) log("handleSetAcceptUnvalidated network=" + network + " accept=" + accept + " always=" + always); @@ -2760,6 +2773,20 @@ public class ConnectivityService extends IConnectivityManager.Stub } + private void handleSetAvoidUnvalidated(Network network) { + NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network); + if (nai == null || nai.lastValidated) { + // Nothing to do. The network either disconnected or revalidated. + return; + } + if (!nai.avoidUnvalidated) { + int oldScore = nai.getCurrentScore(); + nai.avoidUnvalidated = true; + rematchAllNetworksAndRequests(nai, oldScore); + sendUpdatedScoreToFactories(nai); + } + } + private void scheduleUnvalidatedPrompt(NetworkAgentInfo nai) { if (VDBG) log("scheduleUnvalidatedPrompt " + nai.network); mHandler.sendMessageDelayed( @@ -2767,31 +2794,70 @@ public class ConnectivityService extends IConnectivityManager.Stub PROMPT_UNVALIDATED_DELAY_MS); } - private boolean mAvoidBadWifi; + private boolean mAvoidBadWifi = true; public boolean avoidBadWifi() { return mAvoidBadWifi; } @VisibleForTesting - public boolean updateAvoidBadWifi() { - // There are two modes: either we always automatically avoid unvalidated wifi, or we show a - // dialog and don't switch to it. The behaviour is controlled by the NETWORK_AVOID_BAD_WIFI - // setting. If the setting has no value, then the value is taken from the config value, - // which can be changed via OEM/carrier overlays. - // - // The only valid values for NETWORK_AVOID_BAD_WIFI are null and unset. Currently, the unit - // test uses 0 in order to avoid having to mock out fetching the carrier setting. - int defaultAvoidBadWifi = - mContext.getResources().getInteger(R.integer.config_networkAvoidBadWifi); - int avoid = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.NETWORK_AVOID_BAD_WIFI, defaultAvoidBadWifi); + /** Whether the device or carrier configuration disables avoiding bad wifi by default. */ + public boolean configRestrictsAvoidBadWifi() { + return mContext.getResources().getInteger(R.integer.config_networkAvoidBadWifi) == 0; + } + + /** Whether we should display a notification when wifi becomes unvalidated. */ + public boolean shouldNotifyWifiUnvalidated() { + return configRestrictsAvoidBadWifi() && + Settings.Global.getString(mContext.getContentResolver(), + Settings.Global.NETWORK_AVOID_BAD_WIFI) == null; + } + + private boolean updateAvoidBadWifi() { + boolean settingAvoidBadWifi = "1".equals(Settings.Global.getString( + mContext.getContentResolver(), Settings.Global.NETWORK_AVOID_BAD_WIFI)); boolean prev = mAvoidBadWifi; - mAvoidBadWifi = (avoid == 1); + mAvoidBadWifi = settingAvoidBadWifi || !configRestrictsAvoidBadWifi(); return mAvoidBadWifi != prev; } + private void dumpAvoidBadWifiSettings(IndentingPrintWriter pw) { + boolean configRestrict = configRestrictsAvoidBadWifi(); + if (!configRestrict) { + pw.println("Bad Wi-Fi avoidance: unrestricted"); + return; + } + + pw.println("Bad Wi-Fi avoidance: " + avoidBadWifi()); + pw.increaseIndent(); + pw.println("Config restrict: " + configRestrict); + + String value = Settings.Global.getString( + mContext.getContentResolver(), Settings.Global.NETWORK_AVOID_BAD_WIFI); + String description; + // Can't use a switch statement because strings are legal case labels, but null is not. + if ("0".equals(value)) { + description = "get stuck"; + } else if (value == null) { + description = "prompt"; + } else if ("1".equals(value)) { + description = "avoid"; + } else { + description = value + " (?)"; + } + pw.println("User setting: " + description); + pw.println("Network overrides:"); + pw.increaseIndent(); + for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { + if (nai.avoidUnvalidated) { + pw.println(nai.name()); + } + } + pw.decreaseIndent(); + pw.decreaseIndent(); + } + private void showValidationNotification(NetworkAgentInfo nai, NotificationType type) { final String action; switch (type) { @@ -2841,7 +2907,7 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkCapabilities nc = nai.networkCapabilities; if (DBG) log("handleNetworkUnvalidated " + nai.name() + " cap=" + nc); - if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) && !avoidBadWifi()) { + if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) && shouldNotifyWifiUnvalidated()) { showValidationNotification(nai, NotificationType.LOST_INTERNET); } } @@ -2924,6 +2990,10 @@ public class ConnectivityService extends IConnectivityManager.Stub handleSetAcceptUnvalidated((Network) msg.obj, msg.arg1 != 0, msg.arg2 != 0); break; } + case EVENT_SET_AVOID_UNVALIDATED: { + handleSetAvoidUnvalidated((Network) msg.obj); + break; + } case EVENT_PROMPT_UNVALIDATED: { handlePromptUnvalidated((Network) msg.obj); break; diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java index 383e25a6d293..2ff036be4b4b 100644 --- a/services/core/java/com/android/server/ServiceWatcher.java +++ b/services/core/java/com/android/server/ServiceWatcher.java @@ -141,8 +141,12 @@ public class ServiceWatcher implements ServiceConnection { * <p> * Note that if there are no matching encryption-aware services, we may not * bind to a real service until after the current user is unlocked. + * + * @returns {@code true} if a potential service implementation was found. */ public boolean start() { + if (isServiceMissing()) return false; + synchronized (mLock) { bindBestPackageLocked(mServicePackageName, false); } @@ -174,6 +178,17 @@ public class ServiceWatcher implements ServiceConnection { } /** + * Check if any instance of this service is present on the device, + * regardless of it being encryption-aware or not. + */ + private boolean isServiceMissing() { + final Intent intent = new Intent(mAction); + final int flags = PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE; + return mPm.queryIntentServicesAsUser(intent, flags, mCurrentUserId).isEmpty(); + } + + /** * Searches and binds to the best package, or do nothing if the best package * is already bound, unless force rebinding is requested. * @@ -181,7 +196,7 @@ public class ServiceWatcher implements ServiceConnection { * packages if it is {@code null}. * @param forceRebind Force a rebinding to the best package if it's already * bound. - * @return {@code true} if a valid package was found to bind to. + * @returns {@code true} if a valid package was found to bind to. */ private boolean bindBestPackageLocked(String justCheckThisPackage, boolean forceRebind) { Intent intent = new Intent(mAction); diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java index 4b0d4be11b14..4f02a23d1e76 100644 --- a/services/core/java/com/android/server/TextServicesManagerService.java +++ b/services/core/java/com/android/server/TextServicesManagerService.java @@ -459,71 +459,75 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { if (!calledFromValidUser()) { return null; } + final int subtypeHashCode; + final SpellCheckerInfo sci; + final Locale systemLocale; synchronized (mSpellCheckerMap) { - final int subtypeHashCode = + subtypeHashCode = mSettings.getSelectedSpellCheckerSubtype(SpellCheckerSubtype.SUBTYPE_ID_NONE); if (DBG) { Slog.w(TAG, "getCurrentSpellCheckerSubtype: " + subtypeHashCode); } - final SpellCheckerInfo sci = getCurrentSpellChecker(null); - if (sci == null || sci.getSubtypeCount() == 0) { - if (DBG) { - Slog.w(TAG, "Subtype not found."); + sci = getCurrentSpellChecker(null); + systemLocale = mContext.getResources().getConfiguration().locale; + } + if (sci == null || sci.getSubtypeCount() == 0) { + if (DBG) { + Slog.w(TAG, "Subtype not found."); + } + return null; + } + if (subtypeHashCode == SpellCheckerSubtype.SUBTYPE_ID_NONE + && !allowImplicitlySelectedSubtype) { + return null; + } + String candidateLocale = null; + if (subtypeHashCode == 0) { + // Spell checker language settings == "auto" + final InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); + if (imm != null) { + final InputMethodSubtype currentInputMethodSubtype = + imm.getCurrentInputMethodSubtype(); + if (currentInputMethodSubtype != null) { + final String localeString = currentInputMethodSubtype.getLocale(); + if (!TextUtils.isEmpty(localeString)) { + // 1. Use keyboard locale if available in the spell checker + candidateLocale = localeString; + } } - return null; } - if (subtypeHashCode == SpellCheckerSubtype.SUBTYPE_ID_NONE - && !allowImplicitlySelectedSubtype) { - return null; + if (candidateLocale == null) { + // 2. Use System locale if available in the spell checker + candidateLocale = systemLocale.toString(); } - String candidateLocale = null; + } + SpellCheckerSubtype candidate = null; + for (int i = 0; i < sci.getSubtypeCount(); ++i) { + final SpellCheckerSubtype scs = sci.getSubtypeAt(i); if (subtypeHashCode == 0) { - // Spell checker language settings == "auto" - final InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); - if (imm != null) { - final InputMethodSubtype currentInputMethodSubtype = - imm.getCurrentInputMethodSubtype(); - if (currentInputMethodSubtype != null) { - final String localeString = currentInputMethodSubtype.getLocale(); - if (!TextUtils.isEmpty(localeString)) { - // 1. Use keyboard locale if available in the spell checker - candidateLocale = localeString; - } + final String scsLocale = scs.getLocale(); + if (candidateLocale.equals(scsLocale)) { + return scs; + } else if (candidate == null) { + if (candidateLocale.length() >= 2 && scsLocale.length() >= 2 + && candidateLocale.startsWith(scsLocale)) { + // Fall back to the applicable language + candidate = scs; } } - if (candidateLocale == null) { - // 2. Use System locale if available in the spell checker - candidateLocale = mContext.getResources().getConfiguration().locale.toString(); - } - } - SpellCheckerSubtype candidate = null; - for (int i = 0; i < sci.getSubtypeCount(); ++i) { - final SpellCheckerSubtype scs = sci.getSubtypeAt(i); - if (subtypeHashCode == 0) { - final String scsLocale = scs.getLocale(); - if (candidateLocale.equals(scsLocale)) { - return scs; - } else if (candidate == null) { - if (candidateLocale.length() >= 2 && scsLocale.length() >= 2 - && candidateLocale.startsWith(scsLocale)) { - // Fall back to the applicable language - candidate = scs; - } - } - } else if (scs.hashCode() == subtypeHashCode) { - if (DBG) { - Slog.w(TAG, "Return subtype " + scs.hashCode() + ", input= " + locale - + ", " + scs.getLocale()); - } - // 3. Use the user specified spell check language - return scs; + } else if (scs.hashCode() == subtypeHashCode) { + if (DBG) { + Slog.w(TAG, "Return subtype " + scs.hashCode() + ", input= " + locale + + ", " + scs.getLocale()); } + // 3. Use the user specified spell check language + return scs; } - // 4. Fall back to the applicable language and return it if not null - // 5. Simply just return it even if it's null which means we could find no suitable - // spell check languages - return candidate; } + // 4. Fall back to the applicable language and return it if not null + // 5. Simply just return it even if it's null which means we could find no suitable + // spell check languages + return candidate; } @Override diff --git a/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java b/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java new file mode 100644 index 000000000000..f4511d28ba46 --- /dev/null +++ b/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2016 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.server.accounts; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AccountManagerInternal; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.Log; +import android.util.PackageUtils; +import android.util.Xml; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.content.PackageMonitor; +import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.XmlUtils; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +/** + * Helper class for backup and restore of account access grants. + */ +public final class AccountManagerBackupHelper { + private static final String TAG = "AccountManagerBackupHelper"; + + private static final long PENDING_RESTORE_TIMEOUT_MILLIS = 60 * 60 * 1000; // 1 hour + + private static final String TAG_PERMISSIONS = "permissions"; + private static final String TAG_PERMISSION = "permission"; + private static final String ATTR_ACCOUNT_SHA_256 = "account-sha-256"; + private static final String ATTR_PACKAGE = "package"; + private static final String ATTR_DIGEST = "digest"; + + private static final String ACCOUNT_ACCESS_GRANTS = "" + + "SELECT " + AccountManagerService.ACCOUNTS_NAME + ", " + + AccountManagerService.GRANTS_GRANTEE_UID + + " FROM " + AccountManagerService.TABLE_ACCOUNTS + + ", " + AccountManagerService.TABLE_GRANTS + + " WHERE " + AccountManagerService.GRANTS_ACCOUNTS_ID + + "=" + AccountManagerService.ACCOUNTS_ID; + + private final Object mLock = new Object(); + + private final AccountManagerService mAccountManagerService; + private final AccountManagerInternal mAccountManagerInternal; + + @GuardedBy("mLock") + private List<PendingAppPermission> mRestorePendingAppPermissions; + + @GuardedBy("mLock") + private RestorePackageMonitor mRestorePackageMonitor; + + @GuardedBy("mLock") + private Runnable mRestoreCancelCommand; + + public AccountManagerBackupHelper(AccountManagerService accountManagerService, + AccountManagerInternal accountManagerInternal) { + mAccountManagerService = accountManagerService; + mAccountManagerInternal = accountManagerInternal; + } + + private final class PendingAppPermission { + private final @NonNull String accountDigest; + private final @NonNull String packageName; + private final @NonNull String certDigest; + private final @IntRange(from = 0) int userId; + + public PendingAppPermission(String accountDigest, String packageName, + String certDigest, int userId) { + this.accountDigest = accountDigest; + this.packageName = packageName; + this.certDigest = certDigest; + this.userId = userId; + } + + public boolean apply(PackageManager packageManager) { + Account account = null; + AccountManagerService.UserAccounts accounts = mAccountManagerService + .getUserAccounts(userId); + synchronized (accounts.cacheLock) { + for (Account[] accountsPerType : accounts.accountCache.values()) { + for (Account accountPerType : accountsPerType) { + if (accountDigest.equals(PackageUtils.computeSha256Digest( + accountPerType.name.getBytes()))) { + account = accountPerType; + break; + } + } + if (account != null) { + break; + } + } + } + if (account == null) { + return false; + } + final PackageInfo packageInfo; + try { + packageInfo = packageManager.getPackageInfoAsUser(packageName, + PackageManager.GET_SIGNATURES, userId); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + String currentCertDigest = PackageUtils.computeCertSha256Digest( + packageInfo.signatures[0]); + if (!certDigest.equals(currentCertDigest)) { + return false; + } + final int uid = packageInfo.applicationInfo.uid; + if (!mAccountManagerInternal.hasAccountAccess(account, uid)) { + mAccountManagerService.grantAppPermission(account, + AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid); + } + return true; + } + } + + public byte[] backupAccountAccessPermissions(int userId) { + final AccountManagerService.UserAccounts accounts = mAccountManagerService + .getUserAccounts(userId); + synchronized (accounts.cacheLock) { + SQLiteDatabase db = accounts.openHelper.getReadableDatabase(); + try ( + Cursor cursor = db.rawQuery(ACCOUNT_ACCESS_GRANTS, null); + ) { + if (cursor == null || !cursor.moveToFirst()) { + return null; + } + + final int nameColumnIdx = cursor.getColumnIndex( + AccountManagerService.ACCOUNTS_NAME); + final int uidColumnIdx = cursor.getColumnIndex( + AccountManagerService.GRANTS_GRANTEE_UID); + + ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); + try { + final XmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(dataStream, StandardCharsets.UTF_8.name()); + serializer.startDocument(null, true); + serializer.startTag(null, TAG_PERMISSIONS); + + PackageManager packageManager = mAccountManagerService.mContext + .getPackageManager(); + + do { + final String accountName = cursor.getString(nameColumnIdx); + final int uid = cursor.getInt(uidColumnIdx); + + final String[] packageNames = packageManager.getPackagesForUid(uid); + if (packageNames == null) { + continue; + } + + for (String packageName : packageNames) { + String digest = PackageUtils.computePackageCertSha256Digest( + packageManager, packageName, userId); + if (digest != null) { + serializer.startTag(null, TAG_PERMISSION); + serializer.attribute(null, ATTR_ACCOUNT_SHA_256, + PackageUtils.computeSha256Digest(accountName.getBytes())); + serializer.attribute(null, ATTR_PACKAGE, packageName); + serializer.attribute(null, ATTR_DIGEST, digest); + serializer.endTag(null, TAG_PERMISSION); + } + } + } while (cursor.moveToNext()); + + serializer.endTag(null, TAG_PERMISSIONS); + serializer.endDocument(); + serializer.flush(); + } catch (IOException e) { + Log.e(TAG, "Error backing up account access grants", e); + return null; + } + + return dataStream.toByteArray(); + } + } + } + + public void restoreAccountAccessPermissions(byte[] data, int userId) { + try { + ByteArrayInputStream dataStream = new ByteArrayInputStream(data); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(dataStream, StandardCharsets.UTF_8.name()); + PackageManager packageManager = mAccountManagerService.mContext.getPackageManager(); + + final int permissionsOuterDepth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, permissionsOuterDepth)) { + if (!TAG_PERMISSIONS.equals(parser.getName())) { + continue; + } + final int permissionOuterDepth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, permissionOuterDepth)) { + if (!TAG_PERMISSION.equals(parser.getName())) { + continue; + } + String accountDigest = parser.getAttributeValue(null, ATTR_ACCOUNT_SHA_256); + if (TextUtils.isEmpty(accountDigest)) { + XmlUtils.skipCurrentTag(parser); + } + String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); + if (TextUtils.isEmpty(packageName)) { + XmlUtils.skipCurrentTag(parser); + } + String digest = parser.getAttributeValue(null, ATTR_DIGEST); + if (TextUtils.isEmpty(digest)) { + XmlUtils.skipCurrentTag(parser); + } + + PendingAppPermission pendingAppPermission = new PendingAppPermission( + accountDigest, packageName, digest, userId); + + if (!pendingAppPermission.apply(packageManager)) { + synchronized (mLock) { + // Start watching before add pending to avoid a missed signal + if (mRestorePackageMonitor == null) { + mRestorePackageMonitor = new RestorePackageMonitor(); + mRestorePackageMonitor.register(mAccountManagerService.mContext, + mAccountManagerService.mHandler.getLooper(), true); + } + if (mRestorePendingAppPermissions == null) { + mRestorePendingAppPermissions = new ArrayList<>(); + } + mRestorePendingAppPermissions.add(pendingAppPermission); + } + } + } + } + + // Make sure we eventually prune the in-memory pending restores + mRestoreCancelCommand = new CancelRestoreCommand(); + mAccountManagerService.mHandler.postDelayed(mRestoreCancelCommand, + PENDING_RESTORE_TIMEOUT_MILLIS); + } catch (XmlPullParserException | IOException e) { + Log.e(TAG, "Error restoring app permissions", e); + } + } + + private final class RestorePackageMonitor extends PackageMonitor { + @Override + public void onPackageAdded(String packageName, int uid) { + synchronized (mLock) { + if (mRestorePendingAppPermissions == null) { + return; + } + if (UserHandle.getUserId(uid) != UserHandle.USER_SYSTEM) { + return; + } + final int count = mRestorePendingAppPermissions.size(); + for (int i = count - 1; i >= 0; i--) { + PendingAppPermission pendingAppPermission = + mRestorePendingAppPermissions.get(i); + if (!pendingAppPermission.packageName.equals(packageName)) { + continue; + } + if (pendingAppPermission.apply( + mAccountManagerService.mContext.getPackageManager())) { + mRestorePendingAppPermissions.remove(i); + } + } + if (mRestorePendingAppPermissions.isEmpty() + && mRestoreCancelCommand != null) { + mAccountManagerService.mHandler.removeCallbacks(mRestoreCancelCommand); + mRestoreCancelCommand.run(); + mRestoreCancelCommand = null; + } + } + } + } + + private final class CancelRestoreCommand implements Runnable { + @Override + public void run() { + synchronized (mLock) { + mRestorePendingAppPermissions = null; + if (mRestorePackageMonitor != null) { + mRestorePackageMonitor.unregister(); + mRestorePackageMonitor = null; + } + } + } + } +} diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index b152372d092d..04c15c8983cc 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -26,12 +26,14 @@ import android.accounts.AccountManagerInternal; import android.accounts.AuthenticatorDescription; import android.accounts.CantAddAccountActivity; import android.accounts.GrantCredentialsPermissionActivity; +import android.accounts.IAccountAccessTracker; import android.accounts.IAccountAuthenticator; import android.accounts.IAccountAuthenticatorResponse; import android.accounts.IAccountManager; import android.accounts.IAccountManagerResponse; import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.ActivityThread; @@ -87,12 +89,14 @@ import android.os.UserManager; import android.os.storage.StorageManager; import android.text.TextUtils; import android.util.Log; +import android.util.PackageUtils; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.PackageMonitor; import com.android.internal.util.ArrayUtils; @@ -120,11 +124,11 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @@ -155,13 +159,6 @@ public class AccountManagerService } @Override - public void onBootPhase(int phase) { - if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { - mService.systemReady(); - } - } - - @Override public void onUnlockUser(int userHandle) { mService.onUnlockUser(userHandle); } @@ -174,13 +171,13 @@ public class AccountManagerService private static final int MAX_DEBUG_DB_SIZE = 64; - private final Context mContext; + final Context mContext; private final PackageManager mPackageManager; private final AppOpsManager mAppOpsManager; private UserManager mUserManager; - private final MessageHandler mHandler; + final MessageHandler mHandler; // Messages that can be sent on mHandler private static final int MESSAGE_TIMED_OUT = 3; @@ -188,9 +185,9 @@ public class AccountManagerService private final IAccountAuthenticatorCache mAuthenticatorCache; - private static final String TABLE_ACCOUNTS = "accounts"; - private static final String ACCOUNTS_ID = "_id"; - private static final String ACCOUNTS_NAME = "name"; + static final String TABLE_ACCOUNTS = "accounts"; + static final String ACCOUNTS_ID = "_id"; + static final String ACCOUNTS_NAME = "name"; private static final String ACCOUNTS_TYPE = "type"; private static final String ACCOUNTS_TYPE_COUNT = "count(type)"; private static final String ACCOUNTS_PASSWORD = "password"; @@ -204,10 +201,10 @@ public class AccountManagerService private static final String AUTHTOKENS_TYPE = "type"; private static final String AUTHTOKENS_AUTHTOKEN = "authtoken"; - private static final String TABLE_GRANTS = "grants"; - private static final String GRANTS_ACCOUNTS_ID = "accounts_id"; + static final String TABLE_GRANTS = "grants"; + static final String GRANTS_ACCOUNTS_ID = "accounts_id"; private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type"; - private static final String GRANTS_GRANTEE_UID = "uid"; + static final String GRANTS_GRANTEE_UID = "uid"; private static final String TABLE_EXTRAS = "extras"; private static final String EXTRAS_ID = "_id"; @@ -276,16 +273,16 @@ public class AccountManagerService static class UserAccounts { private final int userId; - private final DeDatabaseHelper openHelper; + final DeDatabaseHelper openHelper; private final HashMap<Pair<Pair<Account, String>, Integer>, Integer> credentialsPermissionNotificationIds = new HashMap<Pair<Pair<Account, String>, Integer>, Integer>(); private final HashMap<Account, Integer> signinRequiredNotificationIds = new HashMap<Account, Integer>(); - private final Object cacheLock = new Object(); + final Object cacheLock = new Object(); /** protected by the {@link #cacheLock} */ - private final HashMap<String, Account[]> accountCache = - new LinkedHashMap<String, Account[]>(); + final HashMap<String, Account[]> accountCache = + new LinkedHashMap<>(); /** protected by the {@link #cacheLock} */ private final Map<Account, Map<String, String>> userDataCache = new HashMap<>(); /** protected by the {@link #cacheLock} */ @@ -337,6 +334,8 @@ public class AccountManagerService private final SparseArray<UserAccounts> mUsers = new SparseArray<>(); private final SparseBooleanArray mLocalUnlockedUsers = new SparseBooleanArray(); + private CopyOnWriteArrayList<AccountManagerInternal.OnAppPermissionChangeListener> + mAppPermissionChangeListeners = new CopyOnWriteArrayList<>(); private static AtomicReference<AccountManagerService> sThis = new AtomicReference<>(); private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{}; @@ -560,7 +559,7 @@ public class AccountManagerService if (!checkAccess || hasAccountAccess(account, packageName, UserHandle.getUserHandleForUid(uid))) { cancelNotification(getCredentialPermissionNotificationId(account, - AccountManager.ACCOUNT_ACCESS_TOKEN, uid), packageName, + AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName, UserHandle.getUserHandleForUid(uid)); } } @@ -1056,9 +1055,6 @@ public class AccountManagerService } } - public void systemReady() { - } - private UserManager getUserManager() { if (mUserManager == null) { mUserManager = UserManager.get(mContext); @@ -1196,7 +1192,8 @@ public class AccountManagerService final ArrayList<String> accountNames = cur.getValue(); final Account[] accountsForType = new Account[accountNames.size()]; for (int i = 0; i < accountsForType.length; i++) { - accountsForType[i] = new Account(accountNames.get(i), accountType); + accountsForType[i] = new Account(accountNames.get(i), accountType, + new AccountAccessTracker()); } accounts.accountCache.put(accountType, accountsForType); } @@ -1977,6 +1974,8 @@ public class AccountManagerService Bundle result = new Bundle(); result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name); result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type); + result.putBinder(AccountManager.KEY_ACCOUNT_ACCESS_TRACKER, + resultingAccount.getAccessTracker().asBinder()); try { response.onResult(result); } catch (RemoteException e) { @@ -2035,8 +2034,10 @@ public class AccountManagerService /* * Database transaction was successful. Clean up cached * data associated with the account in the user profile. + * The account is now being tracked for remote access. */ - insertAccountIntoCacheLocked(accounts, renamedAccount); + renamedAccount = insertAccountIntoCacheLocked(accounts, renamedAccount); + /* * Extract the data and token caches before removing the * old account to preserve the user data associated with @@ -2324,7 +2325,7 @@ public class AccountManagerService for (Pair<Pair<Account, String>, Integer> key : accounts.credentialsPermissionNotificationIds.keySet()) { if (account.equals(key.first.first) - && AccountManager.ACCOUNT_ACCESS_TOKEN.equals(key.first.second)) { + && AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE.equals(key.first.second)) { final int uid = (Integer) key.second; mHandler.post(() -> cancelAccountAccessRequestNotificationIfNeeded( account, uid, false)); @@ -3887,22 +3888,36 @@ public class AccountManagerService Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete"); try { - final int uid = mPackageManager.getPackageUidAsUser(packageName, userId); - // Use null token which means any token. Having a token means the package - // is trusted by the authenticator, hence it is fine to access the account. - if (permissionIsGranted(account, null, uid, userId)) { - return true; - } - // In addition to the permissions required to get an auth token we also allow - // the account to be accessed by holders of the get accounts permissions. - return checkUidPermission(Manifest.permission.GET_ACCOUNTS_PRIVILEGED, uid, packageName) - || checkUidPermission(Manifest.permission.GET_ACCOUNTS, uid, packageName); + return hasAccountAccess(account, packageName, uid); } catch (NameNotFoundException e) { return false; } } + private boolean hasAccountAccess(@NonNull Account account, @Nullable String packageName, + int uid) { + if (packageName == null) { + String[] packageNames = mPackageManager.getPackagesForUid(uid); + if (ArrayUtils.isEmpty(packageNames)) { + return false; + } + // For app op checks related to permissions all packages in the UID + // have the same app op state, so doesn't matter which one we pick. + packageName = packageNames[0]; + } + + // Use null token which means any token. Having a token means the package + // is trusted by the authenticator, hence it is fine to access the account. + if (permissionIsGranted(account, null, uid, UserHandle.getUserId(uid))) { + return true; + } + // In addition to the permissions required to get an auth token we also allow + // the account to be accessed by holders of the get accounts permissions. + return checkUidPermission(Manifest.permission.GET_ACCOUNTS_PRIVILEGED, uid, packageName) + || checkUidPermission(Manifest.permission.GET_ACCOUNTS, uid, packageName); + } + private boolean checkUidPermission(String permission, int uid, String opPackageName) { final long identity = Binder.clearCallingIdentity(); try { @@ -3978,7 +3993,7 @@ public class AccountManagerService private void handleAuthenticatorResponse(boolean accessGranted) throws RemoteException { cancelNotification(getCredentialPermissionNotificationId(account, - AccountManager.ACCOUNT_ACCESS_TOKEN, uid), packageName, + AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName, UserHandle.getUserHandleForUid(uid)); if (callback != null) { Bundle result = new Bundle(); @@ -3986,7 +4001,7 @@ public class AccountManagerService callback.sendResult(result); } } - }), AccountManager.ACCOUNT_ACCESS_TOKEN, false); + }), AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, false); } @Override @@ -4809,7 +4824,7 @@ public class AccountManagerService } } - private class MessageHandler extends Handler { + class MessageHandler extends Handler { MessageHandler(Looper looper) { super(looper); } @@ -5922,7 +5937,7 @@ public class AccountManagerService * which is in the system. This means we don't need to protect it with permissions. * @hide */ - private void grantAppPermission(Account account, String authTokenType, int uid) { + void grantAppPermission(Account account, String authTokenType, int uid) { if (account == null || authTokenType == null) { Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception()); return; @@ -5939,6 +5954,12 @@ public class AccountManagerService cancelAccountAccessRequestNotificationIfNeeded(account, uid, true); } + + // Listeners are a final CopyOnWriteArrayList, hence no lock needed. + for (AccountManagerInternal.OnAppPermissionChangeListener listener + : mAppPermissionChangeListeners) { + mHandler.post(() -> listener.onAppPermissionChanged(account, uid)); + } } /** @@ -5968,9 +5989,16 @@ public class AccountManagerService } finally { db.endTransaction(); } + cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), new UserHandle(accounts.userId)); } + + // Listeners are a final CopyOnWriteArrayList, hence no lock needed. + for (AccountManagerInternal.OnAppPermissionChangeListener listener + : mAppPermissionChangeListeners) { + mHandler.post(() -> listener.onAppPermissionChanged(account, uid)); + } } static final private String stringArrayToString(String[] value) { @@ -6001,16 +6029,22 @@ public class AccountManagerService /** * This assumes that the caller has already checked that the account is not already present. + * IMPORTANT: The account being inserted will begin to be tracked for access in remote + * processes and if you will return this account to apps you should return the result. + * @return The inserted account which is a new instance that is being tracked. */ - private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) { + private Account insertAccountIntoCacheLocked(UserAccounts accounts, Account account) { Account[] accountsForType = accounts.accountCache.get(account.type); int oldLength = (accountsForType != null) ? accountsForType.length : 0; Account[] newAccountsForType = new Account[oldLength + 1]; if (accountsForType != null) { System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength); } - newAccountsForType[oldLength] = account; + IAccountAccessTracker accessTracker = account.getAccessTracker() != null + ? account.getAccessTracker() : new AccountAccessTracker(); + newAccountsForType[oldLength] = new Account(account, accessTracker); accounts.accountCache.put(account.type, newAccountsForType); + return newAccountsForType[oldLength]; } private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered, @@ -6626,7 +6660,39 @@ public class AccountManagerService } } + private final class AccountAccessTracker extends IAccountAccessTracker.Stub { + @Override + public void onAccountAccessed() throws RemoteException { + final int uid = Binder.getCallingUid(); + if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) { + return; + } + final int userId = UserHandle.getCallingUserId(); + final long identity = Binder.clearCallingIdentity(); + try { + for (Account account : getAccounts(userId, mContext.getOpPackageName())) { + IAccountAccessTracker accountTracker = account.getAccessTracker(); + if (accountTracker != null && asBinder() == accountTracker.asBinder()) { + // An app just accessed the account. At this point it knows about + // it and there is not need to hide this account from the app. + if (!hasAccountAccess(account, null, uid)) { + updateAppPermission(account, AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, + uid, true); + } + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + } + private final class AccountManagerInternalImpl extends AccountManagerInternal { + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private AccountManagerBackupHelper mBackupHelper; + @Override public void requestAccountAccess(@NonNull Account account, @NonNull String packageName, @IntRange(from = 0) int userId, @NonNull RemoteCallback callback) { @@ -6647,7 +6713,8 @@ public class AccountManagerService return; } - if (hasAccountAccess(account, packageName, new UserHandle(userId))) { + if (AccountManagerService.this.hasAccountAccess(account, packageName, + new UserHandle(userId))) { Bundle result = new Bundle(); result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true); callback.sendResult(result); @@ -6663,7 +6730,44 @@ public class AccountManagerService } Intent intent = newRequestAccountAccessIntent(account, packageName, uid, callback); - doNotification(mUsers.get(userId), account, null, intent, packageName, userId); + final UserAccounts userAccounts; + synchronized (mUsers) { + userAccounts = mUsers.get(userId); + } + doNotification(userAccounts, account, null, intent, packageName, userId); + } + + @Override + public void addOnAppPermissionChangeListener(OnAppPermissionChangeListener listener) { + // Listeners are a final CopyOnWriteArrayList, hence no lock needed. + mAppPermissionChangeListeners.add(listener); + } + + @Override + public boolean hasAccountAccess(@NonNull Account account, @IntRange(from = 0) int uid) { + return AccountManagerService.this.hasAccountAccess(account, null, uid); + } + + @Override + public byte[] backupAccountAccessPermissions(int userId) { + synchronized (mLock) { + if (mBackupHelper == null) { + mBackupHelper = new AccountManagerBackupHelper( + AccountManagerService.this, this); + } + return mBackupHelper.backupAccountAccessPermissions(userId); + } + } + + @Override + public void restoreAccountAccessPermissions(byte[] data, int userId) { + synchronized (mLock) { + if (mBackupHelper == null) { + mBackupHelper = new AccountManagerBackupHelper( + AccountManagerService.this, this); + } + mBackupHelper.restoreAccountAccessPermissions(data, userId); + } } } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index aa536768b6ea..0c8532924af3 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -16,6 +16,7 @@ package com.android.server.am; +import android.os.IDeviceIdentifiersPolicyService; import com.android.internal.telephony.TelephonyIntents; import com.google.android.collect.Lists; import com.google.android.collect.Maps; @@ -1125,11 +1126,14 @@ public final class ActivityManagerService extends ActivityManagerNative */ Configuration mGlobalConfiguration = new Configuration(); + /** Current sequencing integer of the configuration, for skipping old configurations. */ + private int mConfigurationSeq; + /** - * Current sequencing integer of the configuration, for skipping old - * configurations. + * Temp object used when global configuration is updated. It is also sent to outer world + * instead of {@link #mGlobalConfiguration} because we don't trust anyone... */ - private int mConfigurationSeq; + private Configuration mTempGlobalConfig = new Configuration(); boolean mSuppressResizeConfigChanges; @@ -2291,6 +2295,9 @@ public final class ActivityManagerService extends ActivityManagerNative } break; case VR_MODE_CHANGE_MSG: { VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class); + if (vrService == null) { + break; + } final ActivityRecord r = (ActivityRecord) msg.obj; boolean vrMode; ComponentName requestedPackage; @@ -6519,6 +6526,17 @@ public final class ActivityManagerService extends ActivityManagerNative } ProfilerInfo profilerInfo = profileFile == null ? null : new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop); + + // We deprecated Build.SERIAL and only apps that target pre NMR1 + // SDK can see it. Since access to the serial is now behind a + // permission we push down the value. + String buildSerial = Build.UNKNOWN; + if (appInfo.targetSdkVersion <= Build.VERSION_CODES.N_MR1) { + buildSerial = IDeviceIdentifiersPolicyService.Stub.asInterface( + ServiceManager.getService(Context.DEVICE_IDENTIFIERS_SERVICE)) + .getSerial(); + } + thread.bindApplication(processName, appInfo, providers, app.instrumentationClass, profilerInfo, app.instrumentationArguments, app.instrumentationWatcher, app.instrumentationUiAutomationConnection, testMode, @@ -6526,7 +6544,9 @@ public final class ActivityManagerService extends ActivityManagerNative isRestrictedBackupMode || !normalMode, app.persistent, new Configuration(mGlobalConfiguration), app.compat, getCommonServicesLocked(app.isolated), - mCoreSettingsObserver.getCoreSettingsLocked()); + mCoreSettingsObserver.getCoreSettingsLocked(), + buildSerial); + updateLruProcessLocked(app, false, null); app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis(); } catch (Exception e) { @@ -18833,6 +18853,7 @@ public final class ActivityManagerService extends ActivityManagerNative throw new SecurityException(msg); } + @Override public void updateConfiguration(Configuration values) { enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION, "updateConfiguration()"); @@ -18857,10 +18878,12 @@ public final class ActivityManagerService extends ActivityManagerNative } void updateUserConfigurationLocked() { - Configuration configuration = new Configuration(mGlobalConfiguration); + final Configuration configuration = new Configuration(mGlobalConfiguration); + final int currentUserId = mUserController.getCurrentUserIdLocked(); Settings.System.adjustConfigurationForUser(mContext.getContentResolver(), configuration, - mUserController.getCurrentUserIdLocked(), Settings.System.canWrite(mContext)); - updateConfigurationLocked(configuration, null, false); + currentUserId, Settings.System.canWrite(mContext)); + updateConfigurationLocked(configuration, null /* starting */, false /* initLocale */, + false /* persistent */, currentUserId, false /* deferResume */); } boolean updateConfigurationLocked(Configuration values, ActivityRecord starting, @@ -18891,130 +18914,147 @@ public final class ActivityManagerService extends ActivityManagerNative private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting, boolean initLocale, boolean persistent, int userId, boolean deferResume) { int changes = 0; + boolean kept = true; if (mWindowManager != null) { mWindowManager.deferSurfaceLayout(); } - if (values != null) { - Configuration newConfig = new Configuration(mGlobalConfiguration); - changes = newConfig.updateFrom(values); - if (changes != 0) { - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.i(TAG_CONFIGURATION, - "Updating configuration to: " + values); - - EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes); - - if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) { - final LocaleList locales = values.getLocales(); - int bestLocaleIndex = 0; - if (locales.size() > 1) { - if (mSupportedSystemLocales == null) { - mSupportedSystemLocales = - Resources.getSystem().getAssets().getLocales(); - } - bestLocaleIndex = Math.max(0, - locales.getFirstMatchIndex(mSupportedSystemLocales)); - } - SystemProperties.set("persist.sys.locale", - locales.get(bestLocaleIndex).toLanguageTag()); - LocaleList.setDefault(locales, bestLocaleIndex); - mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG, - locales.get(bestLocaleIndex))); - } + try { + if (values != null) { + changes = updateGlobalConfiguration(values, initLocale, persistent, userId, + deferResume); + } - mConfigurationSeq++; - if (mConfigurationSeq <= 0) { - mConfigurationSeq = 1; - } - newConfig.seq = mConfigurationSeq; - mGlobalConfiguration = newConfig; - Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + newConfig); - mUsageStatsService.reportConfigurationChange(newConfig, - mUserController.getCurrentUserIdLocked()); - //mUsageStatsService.noteStartConfig(newConfig); + kept = ensureConfigAndVisibilityAfterUpdate(starting, changes); + } finally { + if (mWindowManager != null) { + mWindowManager.continueSurfaceLayout(); + } + } + return kept; + } - final Configuration configCopy = new Configuration(mGlobalConfiguration); + /** Update default (global) configuration and notify listeners about changes. */ + private int updateGlobalConfiguration(@NonNull Configuration values, boolean initLocale, + boolean persistent, int userId, boolean deferResume) { + mTempGlobalConfig.setTo(mGlobalConfiguration); + final int changes = mTempGlobalConfig.updateFrom(values); + if (changes == 0) { + return 0; + } - // TODO: If our config changes, should we auto dismiss any currently - // showing dialogs? - mShowDialogs = shouldShowDialogs(newConfig, mInVrMode); + if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.i(TAG_CONFIGURATION, + "Updating global configuration to: " + values); - AttributeCache ac = AttributeCache.instance(); - if (ac != null) { - ac.updateConfiguration(configCopy); + EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes); + + if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) { + final LocaleList locales = values.getLocales(); + int bestLocaleIndex = 0; + if (locales.size() > 1) { + if (mSupportedSystemLocales == null) { + mSupportedSystemLocales = Resources.getSystem().getAssets().getLocales(); } + bestLocaleIndex = Math.max(0, locales.getFirstMatchIndex(mSupportedSystemLocales)); + } + SystemProperties.set("persist.sys.locale", + locales.get(bestLocaleIndex).toLanguageTag()); + LocaleList.setDefault(locales, bestLocaleIndex); + mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG, + locales.get(bestLocaleIndex))); + } - // Make sure all resources in our process are updated - // right now, so that anyone who is going to retrieve - // resource values after we return will be sure to get - // the new ones. This is especially important during - // boot, where the first config change needs to guarantee - // all resources have that config before following boot - // code is executed. - mSystemThread.applyConfigurationToResources(configCopy); + mConfigurationSeq = Math.max(++mConfigurationSeq, 1); + mTempGlobalConfig.seq = mConfigurationSeq; - if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) { - Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG); - msg.obj = new Configuration(configCopy); - msg.arg1 = userId; - mHandler.sendMessage(msg); - } + mGlobalConfiguration.setTo(mTempGlobalConfig); + Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempGlobalConfig); + // TODO(multi-display): Update UsageEvents#Event to include displayId. + mUsageStatsService.reportConfigurationChange(mTempGlobalConfig, + mUserController.getCurrentUserIdLocked()); - final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0; - if (isDensityChange) { - // Reset the unsupported display size dialog. - mUiHandler.sendEmptyMessage(SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG_MSG); + // TODO: If our config changes, should we auto dismiss any currently showing dialogs? + mShowDialogs = shouldShowDialogs(mTempGlobalConfig, mInVrMode); - killAllBackgroundProcessesExcept(Build.VERSION_CODES.N, - ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); - } + AttributeCache ac = AttributeCache.instance(); + if (ac != null) { + ac.updateConfiguration(mTempGlobalConfig); + } - for (int i=mLruProcesses.size()-1; i>=0; i--) { - ProcessRecord app = mLruProcesses.get(i); - try { - if (app.thread != null) { - if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc " - + app.processName + " new config " + mGlobalConfiguration); - app.thread.scheduleConfigurationChanged(configCopy); - } - } catch (Exception e) { - } + // Make sure all resources in our process are updated right now, so that anyone who is going + // to retrieve resource values after we return will be sure to get the new ones. This is + // especially important during boot, where the first config change needs to guarantee all + // resources have that config before following boot code is executed. + mSystemThread.applyConfigurationToResources(mTempGlobalConfig); + + // We need another copy of global config because we're scheduling some calls instead of + // running them in place. We need to be sure that object we send will be handled unchanged. + final Configuration configCopy = new Configuration(mGlobalConfiguration); + if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) { + Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG); + msg.obj = configCopy; + msg.arg1 = userId; + mHandler.sendMessage(msg); + } + + // TODO(multi-display): Clear also on secondary display density change? + final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0; + if (isDensityChange) { + // Reset the unsupported display size dialog. + mUiHandler.sendEmptyMessage(SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG_MSG); + + killAllBackgroundProcessesExcept(Build.VERSION_CODES.N, + ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); + } + + for (int i = mLruProcesses.size() - 1; i >= 0; i--) { + ProcessRecord app = mLruProcesses.get(i); + try { + if (app.thread != null) { + if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc " + + app.processName + " new config " + configCopy); + app.thread.scheduleConfigurationChanged(configCopy); } - Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY - | Intent.FLAG_RECEIVER_REPLACE_PENDING - | Intent.FLAG_RECEIVER_FOREGROUND); - broadcastIntentLocked(null, null, intent, null, null, 0, null, null, - null, AppOpsManager.OP_NONE, null, false, false, - MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); - if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) { - intent = new Intent(Intent.ACTION_LOCALE_CHANGED); - intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); - if (!mProcessesReady) { - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - } - broadcastIntentLocked(null, null, intent, - null, null, 0, null, null, null, AppOpsManager.OP_NONE, - null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); - } - } - // Update the configuration with WM first and check if any of the stacks need to be - // resized due to the configuration change. If so, resize the stacks now and do any - // relaunches if necessary. This way we don't need to relaunch again below in - // ensureActivityConfigurationLocked(). - if (mWindowManager != null) { - final int[] resizedStacks = - mWindowManager.setNewConfiguration(mGlobalConfiguration); - if (resizedStacks != null) { - for (int stackId : resizedStacks) { - final Rect newBounds = mWindowManager.getBoundsForNewConfiguration(stackId); - mStackSupervisor.resizeStackLocked( - stackId, newBounds, null, null, false, false, deferResume); - } + } catch (Exception e) { + } + } + + Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_REPLACE_PENDING + | Intent.FLAG_RECEIVER_FOREGROUND); + broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, + AppOpsManager.OP_NONE, null, false, false, MY_PID, Process.SYSTEM_UID, + UserHandle.USER_ALL); + if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) { + intent = new Intent(Intent.ACTION_LOCALE_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + if (!mProcessesReady) { + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + } + broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, + AppOpsManager.OP_NONE, null, false, false, MY_PID, Process.SYSTEM_UID, + UserHandle.USER_ALL); + } + + // Update the configuration with WM first and check if any of the stacks need to be resized + // due to the configuration change. If so, resize the stacks now and do any relaunches if + // necessary. This way we don't need to relaunch again afterwards in + // ensureActivityConfigurationLocked(). + if (mWindowManager != null) { + final int[] resizedStacks = + mWindowManager.setNewConfiguration(mTempGlobalConfig); + if (resizedStacks != null) { + for (int stackId : resizedStacks) { + resizeStackWithBoundsFromWindowManager(stackId, deferResume); } } } + return changes; + } + + /** Applies latest configuration and/or visibility updates if needed. */ + private boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) { boolean kept = true; final ActivityStack mainStack = mStackSupervisor.getFocusedStack(); // mainStack is null during startup. @@ -19034,12 +19074,18 @@ public final class ActivityManagerService extends ActivityManagerNative !PRESERVE_WINDOWS); } } - if (mWindowManager != null) { - mWindowManager.continueSurfaceLayout(); - } + return kept; } + /** Helper method that requests bounds from WM and applies them to stack. */ + private void resizeStackWithBoundsFromWindowManager(int stackId, boolean deferResume) { + final Rect newBounds = mWindowManager.getBoundsForNewConfiguration(stackId); + mStackSupervisor.resizeStackLocked( + stackId, newBounds, null /* tempTaskBounds */, null /* tempTaskInsetBounds */, + false /* preserveWindows */, false /* allowResizeInDockedMode */, deferResume); + } + /** * Decide based on the configuration whether we should show the ANR, * crash, etc dialogs. The idea is that if there is no affordance to diff --git a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java index 05f1a6e6a3a4..168ce858eca5 100644 --- a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java +++ b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java @@ -56,8 +56,8 @@ public class MetricsLoggerService extends SystemService { if (DBG) Log.d(TAG, "onBootPhase: PHASE_SYSTEM_SERVICES_READY"); publishBinderService(ConnectivityMetricsLogger.CONNECTIVITY_METRICS_LOGGER_SERVICE, mBinder); - mDnsListener = new DnsEventListenerService(getContext()); - publishBinderService(mDnsListener.SERVICE_NAME, mDnsListener); + mNetdListener = new NetdEventListenerService(getContext()); + publishBinderService(mNetdListener.SERVICE_NAME, mNetdListener); } } @@ -86,7 +86,7 @@ public class MetricsLoggerService extends SystemService { private final ArrayDeque<ConnectivityMetricsEvent> mEvents = new ArrayDeque<>(); - private DnsEventListenerService mDnsListener; + private NetdEventListenerService mNetdListener; private void enforceConnectivityInternalPermission() { getContext().enforceCallingOrSelfPermission( @@ -221,8 +221,8 @@ public class MetricsLoggerService extends SystemService { } pw.println(); - if (mDnsListener != null) { - mDnsListener.dump(pw); + if (mNetdListener != null) { + mNetdListener.dump(pw); } } diff --git a/services/core/java/com/android/server/connectivity/DnsEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java index 8d206ef90b94..e7198d3e7ee3 100644 --- a/services/core/java/com/android/server/connectivity/DnsEventListenerService.java +++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java @@ -22,8 +22,8 @@ import android.net.ConnectivityManager.NetworkCallback; import android.net.Network; import android.net.NetworkRequest; import android.net.metrics.DnsEvent; -import android.net.metrics.IDnsEventListener; import android.net.metrics.IpConnectivityLog; +import android.net.metrics.INetdEventListener; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -37,13 +37,13 @@ import java.util.TreeMap; /** - * Implementation of the IDnsEventListener interface. + * Implementation of the INetdEventListener interface. */ -public class DnsEventListenerService extends IDnsEventListener.Stub { +public class NetdEventListenerService extends INetdEventListener.Stub { - public static final String SERVICE_NAME = "dns_listener"; + public static final String SERVICE_NAME = "netd_listener"; - private static final String TAG = DnsEventListenerService.class.getSimpleName(); + private static final String TAG = NetdEventListenerService.class.getSimpleName(); private static final boolean DBG = true; private static final boolean VDBG = false; @@ -110,7 +110,7 @@ public class DnsEventListenerService extends IDnsEventListener.Stub { private final NetworkCallback mNetworkCallback = new NetworkCallback() { @Override public void onLost(Network network) { - synchronized (DnsEventListenerService.this) { + synchronized (NetdEventListenerService.this) { DnsEventBatch batch = mEventBatches.remove(network.netId); if (batch != null) { batch.logAndClear(); @@ -119,12 +119,12 @@ public class DnsEventListenerService extends IDnsEventListener.Stub { } }; - public DnsEventListenerService(Context context) { + public NetdEventListenerService(Context context) { this(context.getSystemService(ConnectivityManager.class), new IpConnectivityLog()); } @VisibleForTesting - public DnsEventListenerService(ConnectivityManager cm, IpConnectivityLog log) { + public NetdEventListenerService(ConnectivityManager cm, IpConnectivityLog log) { // We are started when boot is complete, so ConnectivityService should already be running. mCm = cm; mMetricsLog = log; diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index cb4bb8840b5a..2a618bcc2eac 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -140,12 +140,14 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { public boolean everValidated; // The result of the last validation attempt on this network (true if validated, false if not). - // This bit exists only because we never unvalidate a network once it's been validated, and that - // is because the network scoring and revalidation code does not (may not?) deal properly with - // networks becoming unvalidated. - // TODO: Fix the network scoring code, remove this, and rename everValidated to validated. public boolean lastValidated; + // If true, becoming unvalidated will lower the network's score. This is only meaningful if the + // system is configured not to do this for certain networks, e.g., if the + // config_networkAvoidBadWifi option is set to 0 and the user has not overridden that via + // Settings.Global.NETWORK_AVOID_BAD_WIFI. + public boolean avoidUnvalidated; + // Whether a captive portal was ever detected on this network. // This is a sticky bit; once set it is never cleared. public boolean everCaptivePortalDetected; @@ -426,8 +428,10 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // Return true on devices configured to ignore score penalty for wifi networks // that become unvalidated (b/31075769). private boolean ignoreWifiUnvalidationPenalty() { - boolean isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI); - return isWifi && !mConnService.avoidBadWifi() && everValidated; + boolean isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) && + networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + boolean avoidBadWifi = mConnService.avoidBadWifi() || avoidUnvalidated; + return isWifi && !avoidBadWifi && everValidated; } // Get the current score for this Network. This may be modified from what the diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java index 9e7cb939cdf0..6ca4e271ec55 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java @@ -141,10 +141,17 @@ public class TetherInterfaceStateMachine extends StateMachine { if (ifcg != null) { InetAddress addr = NetworkUtils.numericToInetAddress(ipAsString); ifcg.setLinkAddress(new LinkAddress(addr, prefixLen)); - if (enabled) { - ifcg.setInterfaceUp(); + if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) { + // The WiFi stack has ownership of the interface up/down state. + // It is unclear whether the bluetooth or USB stacks will manage their own + // state. + ifcg.ignoreInterfaceUpDownStatus(); } else { - ifcg.setInterfaceDown(); + if (enabled) { + ifcg.setInterfaceUp(); + } else { + ifcg.setInterfaceDown(); + } } ifcg.clearFlag("running"); mNMService.setInterfaceConfig(mIfaceName, ifcg); diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index 12955f5771d2..4e236d164a69 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -482,7 +482,7 @@ public final class ContentService extends IContentService.Stub { SyncManager syncManager = getSyncManager(); if (syncManager != null) { syncManager.scheduleSync(account, userId, uId, authority, extras, - false /* onlyThoseWithUnkownSyncableState */); + SyncStorageEngine.AuthorityInfo.UNDEFINED); } } finally { restoreCallingIdentity(identityToken); @@ -548,7 +548,7 @@ public final class ContentService extends IContentService.Stub { } else { syncManager.scheduleSync( request.getAccount(), userId, callerUid, request.getProvider(), extras, - false /* onlyThoseWithUnknownSyncableState */); + SyncStorageEngine.AuthorityInfo.UNDEFINED); } } finally { restoreCallingIdentity(identityToken); diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index ef3c4b2a27ad..2d6bef4fb735 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -48,6 +48,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.PackageManagerInternal; import android.content.pm.ProviderInfo; import android.content.pm.RegisteredServicesCache; import android.content.pm.RegisteredServicesCacheListener; @@ -324,6 +325,8 @@ public class SyncManager { private final AccountManagerInternal mAccountManagerInternal; + private final PackageManagerInternal mPackageManagerInternal; + private List<UserInfo> getAllUsers() { return mUserManager.getUsers(); } @@ -504,7 +507,7 @@ public class SyncManager { @Override public void onSyncRequest(SyncStorageEngine.EndPoint info, int reason, Bundle extras) { scheduleSync(info.account, info.userId, reason, info.provider, extras, - false); + AuthorityInfo.UNDEFINED); } }); @@ -534,7 +537,7 @@ public class SyncManager { if (!removed) { scheduleSync(null, UserHandle.USER_ALL, SyncOperation.REASON_SERVICE_CHANGED, - type.authority, null, false /* onlyThoseWithUnkownSyncableState */); + type.authority, null, AuthorityInfo.UNDEFINED); } } }, mSyncHandler); @@ -575,6 +578,16 @@ public class SyncManager { mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); mAccountManager = (AccountManager) mContext.getSystemService(Context.ACCOUNT_SERVICE); mAccountManagerInternal = LocalServices.getService(AccountManagerInternal.class); + mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); + + mAccountManagerInternal.addOnAppPermissionChangeListener((Account account, int uid) -> { + // If the UID gained access to the account kick-off syncs lacking account access + if (mAccountManagerInternal.hasAccountAccess(account, uid)) { + scheduleSync(account, UserHandle.getUserId(uid), + SyncOperation.REASON_ACCOUNTS_UPDATED, + null, null, AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS); + } + }); mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService( BatteryStats.SERVICE_NAME)); @@ -671,7 +684,7 @@ public class SyncManager { service.type.accountType, userHandle)) { if (!canAccessAccount(account, packageName, userId)) { mAccountManager.updateAppPermission(account, - AccountManager.ACCOUNT_ACCESS_TOKEN, service.uid, true); + AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, service.uid, true); } } } @@ -778,10 +791,11 @@ public class SyncManager { * @param extras a Map of SyncAdapter-specific information to control * syncs of a specific provider. Can be null. Is ignored * if the url is null. - * @param onlyThoseWithUnkownSyncableState Only sync authorities that have unknown state. + * @param targetSyncState Only sync authorities that have the specified sync state. + * Use {@link AuthorityInfo#UNDEFINED} to sync all authorities. */ public void scheduleSync(Account requestedAccount, int userId, int reason, - String requestedAuthority, Bundle extras, boolean onlyThoseWithUnkownSyncableState) { + String requestedAuthority, Bundle extras, int targetSyncState) { final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); if (extras == null) { extras = new Bundle(); @@ -881,14 +895,18 @@ public class SyncManager { + "isSyncable == SYNCABLE_NO_ACCOUNT_ACCESS"); } Bundle finalExtras = new Bundle(extras); + String packageName = syncAdapterInfo.componentName.getPackageName(); + // If the app did not run and has no account access, done + if (!mPackageManagerInternal.wasPackageEverLaunched(packageName, userId)) { + continue; + } mAccountManagerInternal.requestAccountAccess(account.account, - syncAdapterInfo.componentName.getPackageName(), - UserHandle.getUserId(owningUid), + packageName, userId, new RemoteCallback((Bundle result) -> { if (result != null && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) { scheduleSync(account.account, userId, reason, authority, - finalExtras, onlyThoseWithUnkownSyncableState); + finalExtras, targetSyncState); } } )); @@ -903,9 +921,10 @@ public class SyncManager { isSyncable = AuthorityInfo.SYNCABLE; } - if (onlyThoseWithUnkownSyncableState && isSyncable >= 0) { + if (targetSyncState != AuthorityInfo.UNDEFINED && targetSyncState != isSyncable) { continue; } + if (!syncAdapterInfo.type.supportsUploading() && uploadOnly) { continue; } @@ -931,7 +950,7 @@ public class SyncManager { final String owningPackage = syncAdapterInfo.componentName.getPackageName(); - if (isSyncable < 0) { + if (isSyncable == AuthorityInfo.NOT_INITIALIZED) { // Initialisation sync. Bundle newExtras = new Bundle(); newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true); @@ -950,8 +969,8 @@ public class SyncManager { owningUid, owningPackage, reason, source, authority, newExtras, allowParallelSyncs) ); - } - if (!onlyThoseWithUnkownSyncableState) { + } else if (targetSyncState == AuthorityInfo.UNDEFINED + || targetSyncState == isSyncable) { if (isLoggable) { Slog.v(TAG, "scheduleSync:" + " delay until " + delayUntil @@ -1076,7 +1095,7 @@ public class SyncManager { final Bundle extras = new Bundle(); extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true); scheduleSync(account, userId, reason, authority, extras, - false /* onlyThoseWithUnkownSyncableState */); + AuthorityInfo.UNDEFINED); } public SyncAdapterType[] getSyncAdapterTypes(int userId) { @@ -1535,7 +1554,7 @@ public class SyncManager { mContext.getOpPackageName()); for (Account account : accounts) { scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null, - true /* onlyThoseWithUnknownSyncableState */); + AuthorityInfo.NOT_INITIALIZED); } } @@ -2714,7 +2733,8 @@ public class SyncManager { if (syncTargets != null) { scheduleSync(syncTargets.account, syncTargets.userId, - SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider, null, true); + SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider, + null, AuthorityInfo.NOT_INITIALIZED); } } @@ -2786,9 +2806,14 @@ public class SyncManager { final int syncOpState = computeSyncOpState(op); switch (syncOpState) { case SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS: { + String packageName = op.owningPackage; + final int userId = UserHandle.getUserId(op.owningUid); + // If the app did not run and has no account access, done + if (!mPackageManagerInternal.wasPackageEverLaunched(packageName, userId)) { + return; + } mAccountManagerInternal.requestAccountAccess(op.target.account, - op.owningPackage, UserHandle.getUserId(op.owningUid), - new RemoteCallback((Bundle result) -> { + packageName, userId, new RemoteCallback((Bundle result) -> { if (result != null && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) { updateOrAddPeriodicSync(target, pollFrequency, flex, extras); diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java index 8289bae8793b..069ae7394319 100644 --- a/services/core/java/com/android/server/content/SyncStorageEngine.java +++ b/services/core/java/com/android/server/content/SyncStorageEngine.java @@ -211,6 +211,12 @@ public class SyncStorageEngine extends Handler { public static class AuthorityInfo { // Legal values of getIsSyncable + + /** + * The syncable state is undefined. + */ + public static final int UNDEFINED = -2; + /** * Default state for a newly installed adapter. An uninitialized adapter will receive an * initialization sync which are governed by a different set of rules to that of regular diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java index a63399656bab..9fa93f4b8a6c 100644 --- a/services/core/java/com/android/server/dreams/DreamController.java +++ b/services/core/java/com/android/server/dreams/DreamController.java @@ -26,6 +26,7 @@ import android.content.ServiceConnection; import android.os.Binder; import android.os.Handler; import android.os.IBinder; +import android.os.PowerManager; import android.os.RemoteException; import android.os.IBinder.DeathRecipient; import android.os.SystemClock; @@ -116,19 +117,19 @@ final class DreamController { } public void startDream(Binder token, ComponentName name, - boolean isTest, boolean canDoze, int userId) { + boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock) { stopDream(true /*immediate*/); Trace.traceBegin(Trace.TRACE_TAG_POWER, "startDream"); try { - // Close the notification shade. Don't need to send to all, but better to be explicit. + // Close the notification shade. No need to send to all, but better to be explicit. mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL); Slog.i(TAG, "Starting dream: name=" + name + ", isTest=" + isTest + ", canDoze=" + canDoze + ", userId=" + userId); - mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId); + mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId, wakeLock); mDreamStartTime = SystemClock.elapsedRealtime(); MetricsLogger.visible(mContext, @@ -230,6 +231,7 @@ final class DreamController { if (oldDream.mBound) { mContext.unbindService(oldDream); } + oldDream.releaseWakeLockIfNeeded(); try { mIWindowManager.removeWindowToken(oldDream.mToken); @@ -280,6 +282,7 @@ final class DreamController { public final boolean mCanDoze; public final int mUserId; + public PowerManager.WakeLock mWakeLock; public boolean mBound; public boolean mConnected; public IDreamService mService; @@ -288,12 +291,17 @@ final class DreamController { public boolean mWakingGently; public DreamRecord(Binder token, ComponentName name, - boolean isTest, boolean canDoze, int userId) { + boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock) { mToken = token; mName = name; mIsTest = isTest; mCanDoze = canDoze; mUserId = userId; + mWakeLock = wakeLock; + // Hold the lock while we're waiting for the service to connect. Released either when + // DreamService connects (and is then responsible for keeping the device awake) or + // dreaming stops. + mWakeLock.acquire(); } // May be called on any thread. @@ -316,14 +324,25 @@ final class DreamController { mHandler.post(new Runnable() { @Override public void run() { - mConnected = true; - if (mCurrentDream == DreamRecord.this && mService == null) { - attach(IDreamService.Stub.asInterface(service)); + try { + mConnected = true; + if (mCurrentDream == DreamRecord.this && mService == null) { + attach(IDreamService.Stub.asInterface(service)); + } + } finally { + releaseWakeLockIfNeeded(); } } }); } + private void releaseWakeLockIfNeeded() { + if (mWakeLock != null) { + mWakeLock.release(); + mWakeLock = null; + } + } + // May be called on any thread. @Override public void onServiceDisconnected(ComponentName name) { diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index a783fa25ed45..20bccf19f131 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -370,12 +370,10 @@ public final class DreamManagerService extends SystemService { mCurrentDreamCanDoze = canDoze; mCurrentDreamUserId = userId; - mHandler.post(new Runnable() { - @Override - public void run() { - mController.startDream(newToken, name, isTest, canDoze, userId); - } - }); + PowerManager.WakeLock wakeLock = mPowerManager + .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "startDream"); + mHandler.post(wakeLock.wrap( + () -> mController.startDream(newToken, name, isTest, canDoze, userId, wakeLock))); } private void stopDreamLocked(final boolean immediate) { diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java index 07048a491543..ca6481792dfe 100644 --- a/services/core/java/com/android/server/lights/LightsService.java +++ b/services/core/java/com/android/server/lights/LightsService.java @@ -172,10 +172,12 @@ public class LightsService extends SystemService { if (phase == PHASE_SYSTEM_SERVICES_READY) { IVrManager vrManager = (IVrManager) getBinderService(VrManagerService.VR_MANAGER_BINDER_SERVICE); - try { - vrManager.registerListener(mVrStateCallbacks); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to register VR mode state listener: " + e); + if (vrManager != null) { + try { + vrManager.registerListener(mVrStateCallbacks); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to register VR mode state listener: " + e); + } } } } diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 768d338c75fe..4c58ffd5dc83 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -767,6 +767,13 @@ public class MediaSessionService extends SystemService implements Monitor { + "setup is in progress."); return; } + if (isGlobalPriorityActive() && uid != Process.SYSTEM_UID) { + // Prevent dispatching key event through reflection while the global priority + // session is active. + Slog.i(TAG, "Only the system can dispatch media key event " + + "to the global priority session."); + return; + } synchronized (mLock) { // If we don't have a media button receiver to fall back on diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 1eef3a731c56..6c10ffdfcd40 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -2652,9 +2652,31 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + /** + * Toggle the firewall standby chain and inform listeners if the uid rules have effectively + * changed. + */ void updateRulesForAppIdleParoleUL() { - boolean enableChain = !mUsageStats.isAppIdleParoleOn(); + boolean paroled = mUsageStats.isAppIdleParoleOn(); + boolean enableChain = !paroled; enableFirewallChainUL(FIREWALL_CHAIN_STANDBY, enableChain); + + int ruleCount = mUidFirewallStandbyRules.size(); + for (int i = 0; i < ruleCount; i++) { + int uid = mUidFirewallStandbyRules.keyAt(i); + int oldRules = mUidRules.get(uid); + if (enableChain) { + // Chain wasn't enabled before and the other power-related + // chains are whitelists, so we can clear the + // MASK_ALL_NETWORKS part of the rules and re-inform listeners if + // the effective rules result in blocking network access. + oldRules &= MASK_METERED_NETWORKS; + } else { + // Skip if it had no restrictions to begin with + if ((oldRules & MASK_ALL_NETWORKS) == 0) continue; + } + updateRulesForPowerRestrictionsUL(uid, oldRules, paroled); + } } /** @@ -3008,14 +3030,34 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * <strong>NOTE: </strong>This method does not update the firewall rules on {@code netd}. */ private void updateRulesForPowerRestrictionsUL(int uid) { + final int oldUidRules = mUidRules.get(uid, RULE_NONE); + + final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldUidRules, false); + + if (newUidRules == RULE_NONE) { + mUidRules.delete(uid); + } else { + mUidRules.put(uid, newUidRules); + } + } + + /** + * Similar to above but ignores idle state if app standby is currently disabled by parole. + * + * @param uid the uid of the app to update rules for + * @param oldUidRules the current rules for the uid, in order to determine if there's a change + * @param paroled whether to ignore idle state of apps and only look at other restrictions. + * + * @return the new computed rules for the uid + */ + private int updateRulesForPowerRestrictionsUL(int uid, int oldUidRules, boolean paroled) { if (!isUidValidForBlacklistRules(uid)) { if (LOGD) Slog.d(TAG, "no need to update restrict power rules for uid " + uid); - return; + return RULE_NONE; } - final boolean isIdle = isUidIdle(uid); + final boolean isIdle = !paroled && isUidIdle(uid); final boolean restrictMode = isIdle || mRestrictPower || mDeviceIdleMode; - final int oldUidRules = mUidRules.get(uid, RULE_NONE); final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid); final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid); @@ -3049,12 +3091,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { + ", oldUidRules=" + uidRulesToString(oldUidRules)); } - if (newUidRules == RULE_NONE) { - mUidRules.delete(uid); - } else { - mUidRules.put(uid, newUidRules); - } - // Second step: notify listeners if state changed. if (newRule != oldRule) { if (newRule == RULE_NONE || (newRule & RULE_ALLOW_ALL) != 0) { @@ -3071,6 +3107,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget(); } + + return newUidRules; } private class AppIdleStateChangeListener diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 6b2df73de8a6..0c7c8aa8a9b5 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -142,7 +142,6 @@ import com.android.server.lights.LightsManager; import com.android.server.notification.ManagedServices.ManagedServiceInfo; import com.android.server.policy.PhoneWindowManager; import com.android.server.statusbar.StatusBarManagerInternal; -import com.android.server.vr.VrManagerInternal; import com.android.server.notification.ManagedServices.UserProfiles; import libcore.io.IoUtils; @@ -234,7 +233,6 @@ public class NotificationManagerService extends SystemService { AudioManagerInternal mAudioManagerInternal; @Nullable StatusBarManagerInternal mStatusBar; Vibrator mVibrator; - private VrManagerInternal mVrManagerInternal; private WindowManagerInternal mWindowManagerInternal; final IBinder mForegroundToken = new Binder(); @@ -1152,7 +1150,6 @@ public class NotificationManagerService extends SystemService { // Grab our optional AudioService mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); mAudioManagerInternal = getLocalService(AudioManagerInternal.class); - mVrManagerInternal = getLocalService(VrManagerInternal.class); mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); mZenModeHelper.onSystemReady(); } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { diff --git a/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java b/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java new file mode 100644 index 000000000000..759030bd949c --- /dev/null +++ b/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2016 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.server.os; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.os.Binder; +import android.os.Build; +import android.os.IDeviceIdentifiersPolicyService; +import android.os.Process; +import android.os.RemoteException; +import android.os.SystemProperties; +import android.os.UserHandle; +import com.android.server.SystemService; + +/** + * This service defines the policy for accessing device identifiers. + */ +public final class DeviceIdentifiersPolicyService extends SystemService { + public DeviceIdentifiersPolicyService(Context context) { + super(context); + } + + @Override + public void onStart() { + publishBinderService(Context.DEVICE_IDENTIFIERS_SERVICE, + new DeviceIdentifiersPolicy(getContext())); + } + + private static final class DeviceIdentifiersPolicy + extends IDeviceIdentifiersPolicyService.Stub { + private final @NonNull Context mContext; + + public DeviceIdentifiersPolicy(Context context) { + mContext = context; + } + + @Override + public @Nullable String getSerial() throws RemoteException { + if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.READ_PHONE_STATE, "getSerial"); + } + return SystemProperties.get("ro.serialno", Build.UNKNOWN); + } + } +} diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java index 42079fb0df1b..689917cd670a 100644 --- a/services/core/java/com/android/server/pm/OtaDexoptService.java +++ b/services/core/java/com/android/server/pm/OtaDexoptService.java @@ -172,6 +172,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub { Log.i(TAG, "Cleaning up OTA Dexopt state."); } mDexoptCommands = null; + availableSpaceAfterDexopt = getAvailableSpace(); performMetricsLogging(); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 264252012347..51978f22ca5e 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -21134,6 +21134,13 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); public boolean isPackageDataProtected(int userId, String packageName) { return mProtectedPackages.isPackageDataProtected(userId, packageName); } + + @Override + public boolean wasPackageEverLaunched(String packageName, int userId) { + synchronized (mPackages) { + return mSettings.wasPackageEverLaunchedLPr(packageName, userId); + } + } } @Override diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 27ac3ad8852a..8cab355dc3bd 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -4161,6 +4161,14 @@ final class Settings { return pkg.getCurrentEnabledStateLPr(classNameStr, userId); } + boolean wasPackageEverLaunchedLPr(String packageName, int userId) { + final PackageSetting pkgSetting = mPackages.get(packageName); + if (pkgSetting == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + return !pkgSetting.getNotLaunched(userId); + } + boolean setPackageStoppedStateLPw(PackageManagerService pm, String packageName, boolean stopped, boolean allowedByPermission, int uid, int userId) { int appId = UserHandle.getAppId(uid); diff --git a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java index c764833e08a6..9bf04768ba1a 100644 --- a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java +++ b/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java @@ -25,7 +25,9 @@ import android.content.Intent; import android.content.IntentFilter; import android.graphics.PixelFormat; import android.graphics.drawable.ColorDrawable; +import android.os.Binder; import android.os.Handler; +import android.os.IBinder; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; @@ -36,7 +38,6 @@ import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; import android.util.DisplayMetrics; import android.util.Slog; -import android.util.SparseBooleanArray; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; @@ -66,6 +67,7 @@ public class ImmersiveModeConfirmation { private final H mHandler; private final long mShowDelayMs; private final long mPanicThresholdMs; + private final IBinder mWindowToken = new Binder(); private boolean mConfirmed; private ClingWindowView mClingWindow; @@ -190,13 +192,13 @@ public class ImmersiveModeConfirmation { WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, 0 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED , PixelFormat.TRANSLUCENT); lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; lp.setTitle("ImmersiveModeConfirmation"); lp.windowAnimations = com.android.internal.R.style.Animation_ImmersiveModeConfirmation; + lp.token = getWindowToken(); return lp; } @@ -208,6 +210,13 @@ public class ImmersiveModeConfirmation { Gravity.CENTER_HORIZONTAL | Gravity.TOP); } + /** + * @return the window token that's used by all ImmersiveModeConfirmation windows. + */ + public IBinder getWindowToken() { + return mWindowToken; + } + private class ClingWindowView extends FrameLayout { private static final int BGCOLOR = 0x80000000; private static final int OFFSET_DP = 96; diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 61879784a186..b5559382ff2a 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -1932,6 +1932,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { public void setInitialDisplaySize(Display display, int width, int height, int density) { // This method might be called before the policy has been fully initialized // or for other displays we don't care about. + // TODO(multi-display): Define policy for secondary displays. if (mContext == null || display.getDisplayId() != Display.DEFAULT_DISPLAY) { return; } @@ -2027,6 +2028,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public void setDisplayOverscan(Display display, int left, int top, int right, int bottom) { + // TODO(multi-display): Define policy for secondary displays. if (display.getDisplayId() == Display.DEFAULT_DISPLAY) { mOverscanLeft = left; mOverscanTop = top; @@ -2428,6 +2430,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public void onConfigurationChanged() { + // TODO(multi-display): Define policy for secondary displays. final Resources res = mContext.getResources(); mStatusBarHeight = @@ -3964,11 +3967,19 @@ public class PhoneWindowManager implements WindowManagerPolicy { }; @Override + public void setRecentsVisibilityLw(boolean visible) { + mRecentsVisible = visible; + } + + @Override + public void setTvPipVisibilityLw(boolean visible) { + mTvPictureInPictureVisible = visible; + } + + @Override public int adjustSystemUiVisibilityLw(int visibility) { mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility); mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility); - mRecentsVisible = (visibility & View.RECENT_APPS_VISIBLE) > 0; - mTvPictureInPictureVisible = (visibility & View.TV_PICTURE_IN_PICTURE_VISIBLE) > 0; // Reset any bits in mForceClearingStatusBarVisibility that // are now clear. @@ -7533,11 +7544,20 @@ public class PhoneWindowManager implements WindowManagerPolicy { private int updateSystemUiVisibilityLw() { // If there is no window focused, there will be nobody to handle the events // anyway, so just hang on in whatever state we're in until things settle down. - final WindowState win = mFocusedWindow != null ? mFocusedWindow + WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow : mTopFullscreenOpaqueWindowState; - if (win == null) { + if (winCandidate == null) { return 0; } + if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) { + // The immersive mode confirmation should never affect the system bar visibility, + // otherwise it will unhide the navigation bar and hide itself. + winCandidate = isStatusBarKeyguard() ? mStatusBar : mTopFullscreenOpaqueWindowState; + if (winCandidate == null) { + return 0; + } + } + final WindowState win = winCandidate; if ((win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 && mHideLockScreen == true) { // We are updating at a point where the keyguard has gotten // focus, but we were last in a state where the top window is diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index a2b86e3bfd53..3f58c958814d 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -650,10 +650,12 @@ public final class PowerManagerService extends SystemService false, mSettingsObserver, UserHandle.USER_ALL); IVrManager vrManager = (IVrManager) getBinderService(VrManagerService.VR_MANAGER_BINDER_SERVICE); - try { - vrManager.registerListener(mVrStateCallbacks); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to register VR mode state listener: " + e); + if (vrManager != null) { + try { + vrManager.registerListener(mVrStateCallbacks); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to register VR mode state listener: " + e); + } } // Go. readConfigurationLocked(); diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 3075102bc641..3b20fb14c2ce 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -89,6 +89,7 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.JournaledFile; import com.android.server.EventLogTags; +import com.android.server.FgThread; import com.android.server.SystemService; import libcore.io.IoUtils; @@ -589,6 +590,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { class WallpaperConnection extends IWallpaperConnection.Stub implements ServiceConnection { + + /** Time in milliseconds until we expect the wallpaper to reconnect (unless we're in the + * middle of an update). If exceeded, the wallpaper gets reset to the system default. */ + private static final long WALLPAPER_RECONNECT_TIMEOUT_MS = 5000; + final WallpaperInfo mInfo; final Binder mToken = new Binder(); IWallpaperService mService; @@ -599,6 +605,18 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { boolean mDimensionsChanged = false; boolean mPaddingChanged = false; + private Runnable mResetRunnable = () -> { + synchronized (mLock) { + if (!mWallpaper.wallpaperUpdating + && mWallpaper.userId == mCurrentUserId) { + Slog.w(TAG, "Wallpaper reconnect timed out, " + + "reverting to built-in wallpaper!"); + clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, + null); + } + } + }; + public WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper) { mInfo = info; mWallpaper = wallpaper; @@ -615,6 +633,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { // locking there and anyway we always need to be able to // recover if there is something wrong. saveSettingsLocked(mWallpaper.userId); + FgThread.getHandler().removeCallbacks(mResetRunnable); } } } @@ -641,6 +660,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null); } else { mWallpaper.lastDiedTime = SystemClock.uptimeMillis(); + + // If we didn't reset it right away, do so after we couldn't connect to + // it for an extended amount of time to avoid having a black wallpaper. + FgThread.getHandler().removeCallbacks(mResetRunnable); + FgThread.getHandler().postDelayed(mResetRunnable, + WALLPAPER_RECONNECT_TIMEOUT_MS); } final String flattened = name.flattenToString(); EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED, @@ -752,6 +777,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { if (wallpaper.wallpaperComponent != null && wallpaper.wallpaperComponent.getPackageName().equals(packageName)) { wallpaper.wallpaperUpdating = true; + if (wallpaper.connection != null) { + FgThread.getHandler().removeCallbacks( + wallpaper.connection.mResetRunnable); + } } } } diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index f9aa66b825fc..4d12ba946a96 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -564,10 +564,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree allDrawnExcludingSaved = false; } - @Override - void removeWindow(WindowState win) { - super.removeWindow(win); - + void postWindowRemoveStartingWindowCleanup(WindowState win) { // TODO: Something smells about the code below...Is there a better way? if (startingWindow == win) { if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Notify removed startingWindow " + win); @@ -933,7 +930,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree mService.mWindowsChanged = true; if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Removing starting " + tStartingWindow + " from " + fromToken); - fromToken.removeWindow(tStartingWindow); + fromToken.removeChild(tStartingWindow); + fromToken.postWindowRemoveStartingWindowCleanup(tStartingWindow); addWindow(tStartingWindow); // Propagate other interesting state between the tokens. If the old token is displayed, diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index e1aa98d4b79e..4fc3ab0ec503 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -40,6 +40,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP; +import android.annotation.NonNull; import android.app.ActivityManager.StackId; import android.graphics.Rect; import android.graphics.Region; @@ -60,6 +61,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; class DisplayContentList extends ArrayList<DisplayContent> { } @@ -212,6 +214,23 @@ class DisplayContent { return null; } + /** Callback used to notify about configuration changes. */ + void onConfigurationChanged(@NonNull List<Integer> changedStackList) { + // The display size information is heavily dependent on the resources in the current + // configuration, so we need to reconfigure it every time the configuration changes. + // See {@link PhoneWindowManager#setInitialDisplaySize}...sigh... + mService.reconfigureDisplayLocked(this); + + getDockedDividerController().onConfigurationChanged(); + + for (int i = 0; i < mStacks.size(); i++) { + final TaskStack stack = mStacks.get(i); + if (stack.onConfigurationChanged()) { + changedStackList.add(stack.mStackId); + } + } + } + void checkAppWindowsReadyToShow() { for (int i = mStacks.size() - 1; i >= 0; --i) { final TaskStack stack = mStacks.get(i); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 982f12e732d0..9e8c6091909e 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -68,7 +68,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU int mRotation; // Whether mBounds is fullscreen - private boolean mFullscreen = true; + private boolean mFillsParent = true; /** * Contains configurations settings that are different from the parent configuration due to @@ -220,14 +220,14 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU if (bounds != null && Configuration.EMPTY.equals(overrideConfig)) { throw new IllegalArgumentException("non null bounds, but empty configuration"); } - boolean oldFullscreen = mFullscreen; + boolean oldFullscreen = mFillsParent; int rotation = Surface.ROTATION_0; final DisplayContent displayContent = mStack.getDisplayContent(); if (displayContent != null) { displayContent.getLogicalDisplayRect(mTmpRect); rotation = displayContent.getDisplayInfo().rotation; - mFullscreen = bounds == null; - if (mFullscreen) { + mFillsParent = bounds == null; + if (mFillsParent) { bounds = mTmpRect; } } @@ -236,7 +236,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU // Can't set to fullscreen if we don't have a display to get bounds from... return BOUNDS_CHANGE_NONE; } - if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) { + if (mBounds.equals(bounds) && oldFullscreen == mFillsParent && mRotation == rotation) { return BOUNDS_CHANGE_NONE; } @@ -254,7 +254,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU if (displayContent != null) { displayContent.mDimLayerController.updateDimLayer(this); } - mOverrideConfig = mFullscreen ? Configuration.EMPTY : overrideConfig; + mOverrideConfig = mFillsParent ? Configuration.EMPTY : overrideConfig; return boundsChange; } @@ -354,7 +354,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU /** Return true if the current bound can get outputted to the rest of the system as-is. */ private boolean useCurrentBounds() { final DisplayContent displayContent = mStack.getDisplayContent(); - return mFullscreen + return mFillsParent || !StackId.isTaskResizeableByDockedStack(mStack.mStackId) || displayContent == null || displayContent.getDockedStackVisibleForUserLocked() != null; @@ -432,7 +432,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU return; } - if (!mFullscreen) { + if (!mFillsParent) { // When minimizing the docked stack when going home, we don't adjust the task bounds // so we need to intersect the task bounds with the stack bounds here. // @@ -483,7 +483,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU if (displayContent == null) { return; } - if (mFullscreen) { + if (mFillsParent) { setBounds(null, Configuration.EMPTY); return; } @@ -572,7 +572,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU boolean isFullscreen() { if (useCurrentBounds()) { - return mFullscreen; + return mFillsParent; } // The bounds has been adjusted to accommodate for a docked stack, but the docked stack // is not currently visible. Go ahead a represent it as fullscreen to the rest of the @@ -638,7 +638,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU @Override boolean fillsParent() { - return mFullscreen || !StackId.isTaskResizeAllowed(mStack.mStackId); + return mFillsParent || !StackId.isTaskResizeAllowed(mStack.mStackId); } @Override @@ -659,7 +659,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU final String doublePrefix = prefix + " "; pw.println(prefix + "taskId=" + mTaskId); - pw.println(doublePrefix + "mFullscreen=" + mFullscreen); + pw.println(doublePrefix + "mFillsParent=" + mFillsParent); pw.println(doublePrefix + "mBounds=" + mBounds.toShortString()); pw.println(doublePrefix + "mdr=" + mDeferRemoval); pw.println(doublePrefix + "appTokens=" + mChildren); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 0e0cf02951d2..a3c830a48a48 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -886,13 +886,6 @@ public class WindowManagerService extends IWindowManager.Stub // since they won't be notified through the app window animator. final List<IBinder> mNoAnimationNotifyOnTransitionFinished = new ArrayList<>(); - // List of displays to reconfigure after configuration changes. - // Some of the information reported for a display is dependent on resources to do the right - // calculations. For example, {@link DisplayInfo#smallestNominalAppWidth} and company are - // dependent on the height and width of the status and nav bar which change depending on the - // current configuration. - private final DisplayContentList mReconfigureOnConfigurationChanged = new DisplayContentList(); - // State for the RemoteSurfaceTrace system used in testing. If this is enabled SurfaceControl // instances will be replaced with an instance that writes a binary representation of all // commands to mSurfaceTraceFd. @@ -2061,7 +2054,7 @@ public class WindowManagerService extends IWindowManager.Stub final WindowToken token = win.mToken; final AppWindowToken atoken = win.mAppToken; if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Removing " + win + " from " + token); - token.removeWindow(win); + // Window will already be removed from token before this post clean-up method is called. if (token.isEmpty()) { if (!token.explicit) { mTokenMap.remove(token.token); @@ -2074,7 +2067,7 @@ public class WindowManagerService extends IWindowManager.Stub } if (atoken != null) { - atoken.removeWindow(win); + atoken.postWindowRemoveStartingWindowCleanup(win); } if (win.mAttrs.type == TYPE_WALLPAPER) { @@ -3168,30 +3161,19 @@ public class WindowManagerService extends IWindowManager.Stub } } + private int[] onConfigurationChanged() { mPolicy.onConfigurationChanged(); - final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked(); - if (!mReconfigureOnConfigurationChanged.contains(defaultDisplayContent)) { - // The default display size information is heavily dependent on the resources in the - // current configuration, so we need to reconfigure it everytime the configuration - // changes. See {@link PhoneWindowManager#setInitialDisplaySize}...sigh... - mReconfigureOnConfigurationChanged.add(defaultDisplayContent); - } - for (int i = mReconfigureOnConfigurationChanged.size() - 1; i >= 0; i--) { - reconfigureDisplayLocked(mReconfigureOnConfigurationChanged.remove(i)); - } - - defaultDisplayContent.getDockedDividerController().onConfigurationChanged(); mChangedStackList.clear(); - for (int stackNdx = mStackIdToStack.size() - 1; stackNdx >= 0; stackNdx--) { - final TaskStack stack = mStackIdToStack.valueAt(stackNdx); - if (stack.onConfigurationChanged()) { - mChangedStackList.add(stack.mStackId); - } + + final int numDisplays = mDisplayContents.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx); + displayContent.onConfigurationChanged(mChangedStackList); } - return mChangedStackList.isEmpty() ? - null : ArrayUtils.convertToIntArray(mChangedStackList); + + return mChangedStackList.isEmpty() ? null : ArrayUtils.convertToIntArray(mChangedStackList); } @Override @@ -8130,9 +8112,7 @@ public class WindowManagerService extends IWindowManager.Stub reconfigureDisplayLocked(displayContent); } - // displayContent must not be null - private void reconfigureDisplayLocked(DisplayContent displayContent) { - // TODO: Multidisplay: for now only use with default display. + void reconfigureDisplayLocked(@NonNull DisplayContent displayContent) { if (!mDisplayReady) { return; } @@ -8148,9 +8128,6 @@ public class WindowManagerService extends IWindowManager.Stub mWaitingForConfig = true; startFreezingDisplayLocked(false, 0, 0); mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION); - if (!mReconfigureOnConfigurationChanged.contains(displayContent)) { - mReconfigureOnConfigurationChanged.add(displayContent); - } } mWindowPlacerLocked.performSurfacePlacement(); @@ -8892,6 +8869,32 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void setRecentsVisibility(boolean visible) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Caller does not hold permission " + + android.Manifest.permission.STATUS_BAR); + } + + synchronized (mWindowMap) { + mPolicy.setRecentsVisibilityLw(visible); + } + } + + @Override + public void setTvPipVisibility(boolean visible) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Caller does not hold permission " + + android.Manifest.permission.STATUS_BAR); + } + + synchronized (mWindowMap) { + mPolicy.setTvPipVisibilityLw(visible); + } + } + + @Override public void statusBarVisibilityChanged(int visibility) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR) != PackageManager.PERMISSION_GRANTED) { diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 735efa962fac..7ed8e7844e0b 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -91,8 +91,10 @@ class WindowToken extends WindowContainer<WindowState> { final WindowState win = mChildren.get(i); if (DEBUG_WINDOW_MOVEMENT) Slog.w(TAG_WM, "removeAllWindows: removing win=" + win); win.removeIfPossible(); + if (mChildren.contains(win)) { + removeChild(win); + } } - mChildren.clear(); } void setExiting() { @@ -200,11 +202,6 @@ class WindowToken extends WindowContainer<WindowState> { return null; } - @CallSuper - void removeWindow(WindowState win) { - mChildren.remove(win); - } - /** Returns true if the token windows list is empty. */ boolean isEmpty() { return mChildren.isEmpty(); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index c5e2840d9565..96e9cb4ae96c 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -16,7 +16,6 @@ package com.android.server; -import android.app.ActivityManagerNative; import android.app.ActivityThread; import android.app.INotificationManager; import android.app.usage.UsageStatsManagerInternal; @@ -32,7 +31,6 @@ import android.os.Build; import android.os.Environment; import android.os.FactoryTest; import android.os.FileUtils; -import android.os.IPowerManager; import android.os.Looper; import android.os.PowerManager; import android.os.RemoteException; @@ -79,6 +77,7 @@ import com.android.server.media.projection.MediaProjectionManagerService; import com.android.server.net.NetworkPolicyManagerService; import com.android.server.net.NetworkStatsService; import com.android.server.notification.NotificationManagerService; +import com.android.server.os.DeviceIdentifiersPolicyService; import com.android.server.os.SchedulingPolicyService; import com.android.server.pm.BackgroundDexOptService; import com.android.server.pm.Installer; @@ -427,6 +426,12 @@ public final class SystemServer { Installer installer = mSystemServiceManager.startService(Installer.class); traceEnd(); + // In some cases after launching an app we need to access device identifiers, + // therefore register the device identifier policy before the activity manager. + traceBeginAndSlog("DeviceIdentifiersPolicyService"); + mSystemServiceManager.startService(DeviceIdentifiersPolicyService.class); + traceEnd(); + // Activity manager runs the show. traceBeginAndSlog("StartActivityManager"); mActivityManagerService = mSystemServiceManager.startService( @@ -593,6 +598,7 @@ public final class SystemServer { false); boolean disableConsumerIr = SystemProperties.getBoolean("config.disable_consumerir", false); + boolean disableVrManager = SystemProperties.getBoolean("config.disable_vrmanager", false); boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1"); @@ -677,9 +683,11 @@ public final class SystemServer { ServiceManager.addService(Context.INPUT_SERVICE, inputManager); traceEnd(); - traceBeginAndSlog("StartVrManagerService"); - mSystemServiceManager.startService(VrManagerService.class); - traceEnd(); + if (!disableVrManager) { + traceBeginAndSlog("StartVrManagerService"); + mSystemServiceManager.startService(VrManagerService.class); + traceEnd(); + } mActivityManagerService.setWindowManager(wm); diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java index 6d9020390bef..215059d563f6 100644 --- a/services/net/java/android/net/ip/IpManager.java +++ b/services/net/java/android/net/ip/IpManager.java @@ -45,6 +45,7 @@ import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; +import com.android.internal.util.IState; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.server.net.NetlinkTracker; @@ -395,6 +396,7 @@ public class IpManager extends StateMachine { private final WakeupMessage mProvisioningTimeoutAlarm; private final WakeupMessage mDhcpActionTimeoutAlarm; private final LocalLog mLocalLog; + private final MessageHandlingLogger mMsgStateLogger; private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); private NetworkInterface mNetworkInterface; @@ -482,6 +484,7 @@ public class IpManager extends StateMachine { setInitialState(mStoppedState); mLocalLog = new LocalLog(MAX_LOG_RECORDS); + mMsgStateLogger = new MessageHandlingLogger(); super.start(); } @@ -591,9 +594,9 @@ public class IpManager extends StateMachine { @Override protected String getLogRecString(Message msg) { final String logLine = String.format( - "%s/%d %d %d %s", + "%s/%d %d %d %s [%s]", mInterfaceName, mNetworkInterface == null ? -1 : mNetworkInterface.getIndex(), - msg.arg1, msg.arg2, Objects.toString(msg.obj)); + msg.arg1, msg.arg2, Objects.toString(msg.obj), mMsgStateLogger); final String richerLogLine = getWhatToString(msg.what) + " " + logLine; mLocalLog.log(richerLogLine); @@ -601,6 +604,7 @@ public class IpManager extends StateMachine { Log.d(mTag, richerLogLine); } + mMsgStateLogger.reset(); return logLine; } @@ -609,7 +613,11 @@ public class IpManager extends StateMachine { // Don't log EVENT_NETLINK_LINKPROPERTIES_CHANGED. They can be noisy, // and we already log any LinkProperties change that results in an // invocation of IpManager.Callback#onLinkPropertiesChange(). - return (msg.what != EVENT_NETLINK_LINKPROPERTIES_CHANGED); + final boolean shouldLog = (msg.what != EVENT_NETLINK_LINKPROPERTIES_CHANGED); + if (!shouldLog) { + mMsgStateLogger.reset(); + } + return shouldLog; } private void getNetworkInterface() { @@ -965,7 +973,6 @@ public class IpManager extends StateMachine { } } - class StoppedState extends State { @Override public void enter() { @@ -1015,6 +1022,8 @@ public class IpManager extends StateMachine { default: return NOT_HANDLED; } + + mMsgStateLogger.handled(this, getCurrentState()); return HANDLED; } } @@ -1031,6 +1040,13 @@ public class IpManager extends StateMachine { @Override public boolean processMessage(Message msg) { switch (msg.what) { + case CMD_STOP: + break; + + case DhcpClient.CMD_CLEAR_LINKADDRESS: + clearIPv4Address(); + break; + case DhcpClient.CMD_ON_QUIT: mDhcpClient = null; transitionTo(mStoppedState); @@ -1039,6 +1055,8 @@ public class IpManager extends StateMachine { default: deferMessage(msg); } + + mMsgStateLogger.handled(this, getCurrentState()); return HANDLED; } } @@ -1095,6 +1113,8 @@ public class IpManager extends StateMachine { // is EVENT_NETLINK_LINKPROPERTIES_CHANGED (handled above). deferMessage(msg); } + + mMsgStateLogger.handled(this, getCurrentState()); return HANDLED; } @@ -1302,7 +1322,29 @@ public class IpManager extends StateMachine { default: return NOT_HANDLED; } + + mMsgStateLogger.handled(this, getCurrentState()); return HANDLED; } } + + private static class MessageHandlingLogger { + public String processedInState; + public String receivedInState; + + public void reset() { + processedInState = null; + receivedInState = null; + } + + public void handled(State processedIn, IState receivedIn) { + processedInState = processedIn.getClass().getSimpleName(); + receivedInState = receivedIn.getName(); + } + + public String toString() { + return String.format("rcvd_in=%s, proc_in=%s", + receivedInState, processedInState); + } + } } diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java index de90de81a05e..340be6222173 100644 --- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java @@ -82,6 +82,7 @@ import com.android.server.net.NetworkPinner; import java.net.InetAddress; import java.util.ArrayList; +import java.util.Arrays; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; @@ -601,6 +602,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { private class WrappedConnectivityService extends ConnectivityService { private WrappedNetworkMonitor mLastCreatedNetworkMonitor; + public boolean configRestrictsAvoidBadWifi; public WrappedConnectivityService(Context context, INetworkManagementService netManager, INetworkStatsService statsService, INetworkPolicyManager policyManager, @@ -656,6 +658,11 @@ public class ConnectivityServiceTest extends AndroidTestCase { return new FakeWakeupMessage(context, handler, cmdName, cmd, 0, 0, obj); } + @Override + public boolean configRestrictsAvoidBadWifi() { + return configRestrictsAvoidBadWifi; + } + public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() { return mLastCreatedNetworkMonitor; } @@ -2041,8 +2048,48 @@ public class ConnectivityServiceTest extends AndroidTestCase { @SmallTest public void testAvoidBadWifiSetting() throws Exception { + final ContentResolver cr = mServiceContext.getContentResolver(); + final String settingName = Settings.Global.NETWORK_AVOID_BAD_WIFI; + + mService.configRestrictsAvoidBadWifi = false; + String[] values = new String[] {null, "0", "1"}; + for (int i = 0; i < values.length; i++) { + Settings.Global.putInt(cr, settingName, 1); + mService.updateNetworkAvoidBadWifi(); + mService.waitForIdle(); + String msg = String.format("config=false, setting=%s", values[i]); + assertTrue(msg, mService.avoidBadWifi()); + assertFalse(msg, mService.shouldNotifyWifiUnvalidated()); + } + + mService.configRestrictsAvoidBadWifi = true; + + Settings.Global.putInt(cr, settingName, 0); + mService.updateNetworkAvoidBadWifi(); + mService.waitForIdle(); + assertFalse(mService.avoidBadWifi()); + assertFalse(mService.shouldNotifyWifiUnvalidated()); + + Settings.Global.putInt(cr, settingName, 1); + mService.updateNetworkAvoidBadWifi(); + mService.waitForIdle(); + assertTrue(mService.avoidBadWifi()); + assertFalse(mService.shouldNotifyWifiUnvalidated()); + + Settings.Global.putString(cr, settingName, null); + mService.updateNetworkAvoidBadWifi(); + mService.waitForIdle(); + assertFalse(mService.avoidBadWifi()); + assertTrue(mService.shouldNotifyWifiUnvalidated()); + } + + @SmallTest + public void testAvoidBadWifi() throws Exception { ContentResolver cr = mServiceContext.getContentResolver(); + // Pretend we're on a carrier that restricts switching away from bad wifi. + mService.configRestrictsAvoidBadWifi = true; + // File a request for cell to ensure it doesn't go down. final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); final NetworkRequest cellRequest = new NetworkRequest.Builder() @@ -2059,8 +2106,8 @@ public class ConnectivityServiceTest extends AndroidTestCase { TestNetworkCallback validatedWifiCallback = new TestNetworkCallback(); mCm.registerNetworkCallback(validatedWifiRequest, validatedWifiCallback); - // Takes effect on every rematch. Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 0); + mService.updateNetworkAvoidBadWifi(); // Bring up validated cell. mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); @@ -2089,7 +2136,42 @@ public class ConnectivityServiceTest extends AndroidTestCase { NET_CAPABILITY_VALIDATED)); assertEquals(mCm.getActiveNetwork(), wifiNetwork); - // Simulate the user selecting "switch" on the dialog. + // Simulate switching to a carrier that does not restrict avoiding bad wifi, and expect + // that we switch back to cell. + mService.configRestrictsAvoidBadWifi = false; + mService.updateNetworkAvoidBadWifi(); + defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + assertEquals(mCm.getActiveNetwork(), cellNetwork); + + // Switch back to a restrictive carrier. + mService.configRestrictsAvoidBadWifi = true; + mService.updateNetworkAvoidBadWifi(); + defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + assertEquals(mCm.getActiveNetwork(), wifiNetwork); + + // Simulate the user selecting "switch" on the dialog, and check that we switch to cell. + mCm.setAvoidUnvalidated(wifiNetwork); + defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability( + NET_CAPABILITY_VALIDATED)); + assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability( + NET_CAPABILITY_VALIDATED)); + assertEquals(mCm.getActiveNetwork(), cellNetwork); + + // Disconnect and reconnect wifi to clear the one-time switch above. + mWiFiNetworkAgent.disconnect(); + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + validatedWifiCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + wifiNetwork = mWiFiNetworkAgent.getNetwork(); + + // Fail validation on wifi and expect the dialog to appear. + mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 599; + mCm.reportNetworkConnectivity(wifiNetwork, false); + validatedWifiCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); + + // Simulate the user selecting "switch" and checking the don't ask again checkbox. Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1); mService.updateNetworkAvoidBadWifi(); @@ -2101,6 +2183,17 @@ public class ConnectivityServiceTest extends AndroidTestCase { NET_CAPABILITY_VALIDATED)); assertEquals(mCm.getActiveNetwork(), cellNetwork); + // Simulate the user turning the cellular fallback setting off and then on. + // We switch to wifi and then to cell. + Settings.Global.putString(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, null); + mService.updateNetworkAvoidBadWifi(); + defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + assertEquals(mCm.getActiveNetwork(), wifiNetwork); + Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1); + mService.updateNetworkAvoidBadWifi(); + defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + assertEquals(mCm.getActiveNetwork(), cellNetwork); + // If cell goes down, we switch to wifi. mCellNetworkAgent.disconnect(); defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); diff --git a/services/tests/servicestests/src/com/android/server/connectivity/DnsEventListenerServiceTest.java b/services/tests/servicestests/src/com/android/server/connectivity/NetdEventListenerServiceTest.java index 033b2c96c8f5..11cf528458f0 100644 --- a/services/tests/servicestests/src/com/android/server/connectivity/DnsEventListenerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/connectivity/NetdEventListenerServiceTest.java @@ -20,7 +20,7 @@ import android.net.ConnectivityManager.NetworkCallback; import android.net.ConnectivityManager; import android.net.Network; import android.net.metrics.DnsEvent; -import android.net.metrics.IDnsEventListener; +import android.net.metrics.INetdEventListener; import android.net.metrics.IpConnectivityLog; import junit.framework.TestCase; @@ -47,12 +47,12 @@ import java.util.List; import java.util.OptionalInt; import java.util.stream.IntStream; -public class DnsEventListenerServiceTest extends TestCase { +public class NetdEventListenerServiceTest extends TestCase { - // TODO: read from DnsEventListenerService after this constant is read from system property + // TODO: read from NetdEventListenerService after this constant is read from system property static final int BATCH_SIZE = 100; - static final int EVENT_TYPE = IDnsEventListener.EVENT_GETADDRINFO; - // TODO: read from IDnsEventListener + static final int EVENT_TYPE = INetdEventListener.EVENT_GETADDRINFO; + // TODO: read from INetdEventListener static final int RETURN_CODE = 1; static final byte[] EVENT_TYPES = new byte[BATCH_SIZE]; @@ -66,7 +66,7 @@ public class DnsEventListenerServiceTest extends TestCase { } } - DnsEventListenerService mDnsService; + NetdEventListenerService mNetdService; @Mock ConnectivityManager mCm; @Mock IpConnectivityLog mLog; @@ -77,7 +77,7 @@ public class DnsEventListenerServiceTest extends TestCase { MockitoAnnotations.initMocks(this); mCallbackCaptor = ArgumentCaptor.forClass(NetworkCallback.class); mEvCaptor = ArgumentCaptor.forClass(DnsEvent.class); - mDnsService = new DnsEventListenerService(mCm, mLog); + mNetdService = new NetdEventListenerService(mCm, mLog); verify(mCm, times(1)).registerNetworkCallback(any(), mCallbackCaptor.capture()); } @@ -131,7 +131,7 @@ public class DnsEventListenerServiceTest extends TestCase { new Thread() { public void run() { while (System.currentTimeMillis() < stop) { - mDnsService.dump(pw); + mNetdService.dump(pw); } } }.start(); @@ -158,7 +158,7 @@ public class DnsEventListenerServiceTest extends TestCase { void log(int netId, int[] latencies) { for (int l : latencies) { - mDnsService.onDnsEvent(netId, EVENT_TYPE, RETURN_CODE, l); + mNetdService.onDnsEvent(netId, EVENT_TYPE, RETURN_CODE, l); } } diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java index 0afbd0ce5607..fee47831bb0c 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -730,4 +730,14 @@ public class TestWindowManagerPolicy implements WindowManagerPolicy { public boolean shouldRotateSeamlessly(int oldRotation, int newRotation) { return false; } + + @Override + public void setTvPipVisibilityLw(boolean visible) { + + } + + @Override + public void setRecentsVisibilityLw(boolean visible) { + + } } diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java index 4a70060d5097..f4f92ec4d2b2 100644 --- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java +++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java @@ -461,6 +461,16 @@ public class IWindowManagerImpl implements IWindowManager { } @Override + public void setRecentsVisibility(boolean visible) { + // TODO Auto-generated method stub + } + + @Override + public void setTvPipVisibility(boolean visible) { + // TODO Auto-generated method stub + } + + @Override public void stopAppFreezingScreen(IBinder arg0, boolean arg1) throws RemoteException { // TODO Auto-generated method stub } diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java index 06e1b681fbef..5847f798712d 100644 --- a/wifi/java/android/net/wifi/WifiScanner.java +++ b/wifi/java/android/net/wifi/WifiScanner.java @@ -34,9 +34,10 @@ import com.android.internal.util.AsyncChannel; import com.android.internal.util.Preconditions; import com.android.internal.util.Protocol; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; - /** * This class provides a way to scan the Wifi universe around the device * Get an instance of this class by calling @@ -822,6 +823,22 @@ public class WifiScanner { mAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key); } + /** + * Retrieve the most recent scan results from a single scan request. + * {@hide} + */ + public List<ScanResult> getSingleScanResults() { + validateChannel(); + Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SINGLE_SCAN_RESULTS, 0); + if (reply.what == WifiScanner.CMD_OP_SUCCEEDED) { + return Arrays.asList(((ParcelableScanResults) reply.obj).getResults()); + } + OperationResult result = (OperationResult) reply.obj; + Log.e(TAG, "Error retrieving SingleScan results reason: " + result.reason + + " description: " + result.description); + return new ArrayList<ScanResult>(); + } + private void startPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings, int key) { // Bundle up both the settings and send it across. Bundle pnoParams = new Bundle(); @@ -1201,6 +1218,8 @@ public class WifiScanner { public static final int CMD_REGISTER_SCAN_LISTENER = BASE + 27; /** @hide */ public static final int CMD_DEREGISTER_SCAN_LISTENER = BASE + 28; + /** @hide */ + public static final int CMD_GET_SINGLE_SCAN_RESULTS = BASE + 29; private Context mContext; private IWifiScanner mService; diff --git a/wifi/java/android/net/wifi/nan/IWifiNanSessionCallback.aidl b/wifi/java/android/net/wifi/nan/IWifiNanDiscoverySessionCallback.aidl index ff2c409c1dd5..8c1b892f6730 100644 --- a/wifi/java/android/net/wifi/nan/IWifiNanSessionCallback.aidl +++ b/wifi/java/android/net/wifi/nan/IWifiNanDiscoverySessionCallback.aidl @@ -21,7 +21,7 @@ package android.net.wifi.nan; * * {@hide} */ -oneway interface IWifiNanSessionCallback +oneway interface IWifiNanDiscoverySessionCallback { void onSessionStarted(int sessionId); void onSessionConfigSuccess(); diff --git a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl index 17ec1bc240e0..4711bad31f12 100644 --- a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl +++ b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl @@ -19,8 +19,8 @@ package android.net.wifi.nan; import android.app.PendingIntent; import android.net.wifi.nan.ConfigRequest; +import android.net.wifi.nan.IWifiNanDiscoverySessionCallback; import android.net.wifi.nan.IWifiNanEventCallback; -import android.net.wifi.nan.IWifiNanSessionCallback; import android.net.wifi.nan.PublishConfig; import android.net.wifi.nan.SubscribeConfig; import android.net.wifi.RttManager; @@ -42,9 +42,10 @@ interface IWifiNanManager in ConfigRequest configRequest); void disconnect(int clientId, in IBinder binder); - void publish(int clientId, in PublishConfig publishConfig, in IWifiNanSessionCallback callback); + void publish(int clientId, in PublishConfig publishConfig, + in IWifiNanDiscoverySessionCallback callback); void subscribe(int clientId, in SubscribeConfig subscribeConfig, - in IWifiNanSessionCallback callback); + in IWifiNanDiscoverySessionCallback callback); // session API void updatePublish(int clientId, int sessionId, in PublishConfig publishConfig); diff --git a/wifi/java/android/net/wifi/nan/PublishConfig.java b/wifi/java/android/net/wifi/nan/PublishConfig.java index 6203f9539e7c..f988c0b03ca5 100644 --- a/wifi/java/android/net/wifi/nan/PublishConfig.java +++ b/wifi/java/android/net/wifi/nan/PublishConfig.java @@ -32,8 +32,8 @@ import java.util.Arrays; /** * Defines the configuration of a NAN publish session. Built using * {@link PublishConfig.Builder}. A publish session is created using - * {@link WifiNanManager#publish(PublishConfig, WifiNanSessionCallback)} or updated using - * {@link WifiNanPublishSession#updatePublish(PublishConfig)}. + * {@link WifiNanManager#publish(PublishConfig, WifiNanDiscoverySessionCallback)} or updated using + * {@link WifiNanPublishDiscoverySession#updatePublish(PublishConfig)}. * * @hide PROPOSED_NAN_API */ @@ -318,12 +318,13 @@ public final class PublishConfig implements Parcelable { * Sets the number of times an unsolicited (configured using * {@link PublishConfig.Builder#setPublishType(int)}) publish session * will be broadcast. When the count is reached an event will be - * generated for {@link WifiNanSessionCallback#onSessionTerminated(int)} - * with {@link WifiNanSessionCallback#TERMINATE_REASON_DONE} [unless + * generated for {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} + * with {@link WifiNanDiscoverySessionCallback#TERMINATE_REASON_DONE} [unless * {@link #setEnableTerminateNotification(boolean)} disables the callback]. * <p> * Optional. 0 by default - indicating the session doesn't terminate on its own. - * Session will be terminated when {@link WifiNanSession#terminate()} is called. + * Session will be terminated when {@link WifiNanDiscoveryBaseSession#terminate()} is + * called. * * @param publishCount Number of publish packets to broadcast. * @@ -343,12 +344,13 @@ public final class PublishConfig implements Parcelable { * {@link PublishConfig.Builder#setPublishType(int)}) publish session * will be alive - broadcasting a packet. When the TTL is reached * an event will be generated for - * {@link WifiNanSessionCallback#onSessionTerminated(int)} with - * {@link WifiNanSessionCallback#TERMINATE_REASON_DONE} [unless + * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} with + * {@link WifiNanDiscoverySessionCallback#TERMINATE_REASON_DONE} [unless * {@link #setEnableTerminateNotification(boolean)} disables the callback]. * <p> * Optional. 0 by default - indicating the session doesn't terminate on its own. - * Session will be terminated when {@link WifiNanSession#terminate()} is called. + * Session will be terminated when {@link WifiNanDiscoveryBaseSession#terminate()} is + * called. * * @param ttlSec Lifetime of a publish session in seconds. * @@ -365,7 +367,7 @@ public final class PublishConfig implements Parcelable { /** * Configure whether a publish terminate notification - * {@link WifiNanSessionCallback#onSessionTerminated(int)} is reported + * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} is reported * back to the callback. * * @param enable If true the terminate callback will be called when the diff --git a/wifi/java/android/net/wifi/nan/SubscribeConfig.java b/wifi/java/android/net/wifi/nan/SubscribeConfig.java index 5a2c76272f78..47f9398b642f 100644 --- a/wifi/java/android/net/wifi/nan/SubscribeConfig.java +++ b/wifi/java/android/net/wifi/nan/SubscribeConfig.java @@ -32,8 +32,8 @@ import java.util.Arrays; /** * Defines the configuration of a NAN subscribe session. Built using * {@link SubscribeConfig.Builder}. Subscribe is done using - * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanSessionCallback)} or - * {@link WifiNanSubscribeSession#updateSubscribe(SubscribeConfig)}. + * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanDiscoverySessionCallback)} or + * {@link WifiNanSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. * * @hide PROPOSED_NAN_API */ @@ -350,11 +350,12 @@ public final class SubscribeConfig implements Parcelable { * Sets the number of times an active ( * {@link SubscribeConfig.Builder#setSubscribeType(int)}) subscribe session * will broadcast. When the count is reached an event will be - * generated for {@link WifiNanSessionCallback#onSessionTerminated(int)} - * with {@link WifiNanSessionCallback#TERMINATE_REASON_DONE}. + * generated for {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} + * with {@link WifiNanDiscoverySessionCallback#TERMINATE_REASON_DONE}. * <p> * Optional. 0 by default - indicating the session doesn't terminate on its own. - * Session will be terminated when {@link WifiNanSession#terminate()} is called. + * Session will be terminated when {@link WifiNanDiscoveryBaseSession#terminate()} is + * called. * * @param subscribeCount Number of subscribe packets to broadcast. * @@ -374,11 +375,12 @@ public final class SubscribeConfig implements Parcelable { * {@link SubscribeConfig.Builder#setSubscribeType(int)}) subscribe session * will be alive - i.e. broadcasting a packet. When the TTL is reached * an event will be generated for - * {@link WifiNanSessionCallback#onSessionTerminated(int)} with - * {@link WifiNanSessionCallback#TERMINATE_REASON_DONE}. + * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} with + * {@link WifiNanDiscoverySessionCallback#TERMINATE_REASON_DONE}. * <p> * Optional. 0 by default - indicating the session doesn't terminate on its own. - * Session will be terminated when {@link WifiNanSession#terminate()} is called. + * Session will be terminated when {@link WifiNanDiscoveryBaseSession#terminate()} is + * called. * * @param ttlSec Lifetime of a subscribe session in seconds. * @@ -397,7 +399,7 @@ public final class SubscribeConfig implements Parcelable { * Sets the match style of the subscription - how are matches from a * single match session (corresponding to the same publish action on the * peer) reported to the host (using the - * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} + * {@link WifiNanDiscoverySessionCallback#onMatch(int, byte[], byte[])} * ). The options are: only report the first match and ignore the rest * {@link SubscribeConfig#MATCH_STYLE_FIRST_ONLY} or report every single * match {@link SubscribeConfig#MATCH_STYLE_ALL} (the default). @@ -417,7 +419,7 @@ public final class SubscribeConfig implements Parcelable { /** * Configure whether a subscribe terminate notification - * {@link WifiNanSessionCallback#onSessionTerminated(int)} is reported + * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} is reported * back to the callback. * * @param enable If true the terminate callback will be called when the diff --git a/wifi/java/android/net/wifi/nan/WifiNanSession.java b/wifi/java/android/net/wifi/nan/WifiNanDiscoveryBaseSession.java index 005ca291cbf5..58971dbdae28 100644 --- a/wifi/java/android/net/wifi/nan/WifiNanSession.java +++ b/wifi/java/android/net/wifi/nan/WifiNanDiscoveryBaseSession.java @@ -17,7 +17,6 @@ package android.net.wifi.nan; import android.annotation.Nullable; -import android.annotation.SystemApi; import android.net.wifi.RttManager; import android.util.Log; @@ -28,8 +27,8 @@ import java.lang.ref.WeakReference; /** * A class representing a single publish or subscribe NAN session. This object * will not be created directly - only its child classes are available: - * {@link WifiNanPublishSession} and {@link WifiNanSubscribeSession}. This class provides - * functionality common to both publish and subscribe discovery sessions: + * {@link WifiNanPublishDiscoverySession} and {@link WifiNanSubscribeDiscoverySession}. This + * class provides functionality common to both publish and subscribe discovery sessions: * <ul> * <li>Sending messages: {@link #sendMessage(int, byte[], int)} or * {@link #sendMessage(int, byte[], int, int)} methods. @@ -41,8 +40,8 @@ import java.lang.ref.WeakReference; * * @hide PROPOSED_NAN_API */ -public class WifiNanSession { - private static final String TAG = "WifiNanSession"; +public class WifiNanDiscoveryBaseSession { + private static final String TAG = "WifiNanDiscoveryBaseSsn"; private static final boolean DBG = false; private static final boolean VDBG = false; // STOPSHIP if true @@ -68,7 +67,7 @@ public class WifiNanSession { } /** @hide */ - public WifiNanSession(WifiNanManager manager, int sessionId) { + public WifiNanDiscoveryBaseSession(WifiNanManager manager, int sessionId) { if (VDBG) Log.v(TAG, "New client created: manager=" + manager + ", sessionId=" + sessionId); mMgr = new WeakReference<>(manager); @@ -86,7 +85,7 @@ public class WifiNanSession { * This operation must be done on a session which is no longer needed. Otherwise system * resources will continue to be utilized until the application terminates. The only * exception is a session for which we received a termination callback, - * {@link WifiNanSessionCallback#onSessionTerminated(int)}. + * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)}. */ public void terminate() { WifiNanManager mgr = mMgr.get(); @@ -132,20 +131,20 @@ public class WifiNanSession { /** * Sends a message to the specified destination. NAN messages are transmitted in the context * of a discovery session - executed subsequent to a publish/subscribe - * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} event. + * {@link WifiNanDiscoverySessionCallback#onMatch(int, byte[], byte[])} event. * <p> - * NAN messages are not guaranteed delivery. Callbacks on {@link WifiNanSessionCallback} - * indicate message was transmitted successfully, - * {@link WifiNanSessionCallback#onMessageSendSuccess(int)}, or transmission failed + * NAN messages are not guaranteed delivery. Callbacks on + * {@link WifiNanDiscoverySessionCallback} indicate message was transmitted successfully, + * {@link WifiNanDiscoverySessionCallback#onMessageSendSuccess(int)}, or transmission failed * (possibly after several retries) - - * {@link WifiNanSessionCallback#onMessageSendFail(int, int)}. + * {@link WifiNanDiscoverySessionCallback#onMessageSendFail(int, int)}. * <p> * The peer will get a callback indicating a message was received using - * {@link WifiNanSessionCallback#onMessageReceived(int, byte[])}. + * {@link WifiNanDiscoverySessionCallback#onMessageReceived(int, byte[])}. * * @param peerId The peer's ID for the message. Must be a result of an - * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} or - * {@link WifiNanSessionCallback#onMessageReceived(int, byte[])} events. + * {@link WifiNanDiscoverySessionCallback#onMatch(int, byte[], byte[])} or + * {@link WifiNanDiscoverySessionCallback#onMessageReceived(int, byte[])} events. * @param message The message to be transmitted. * @param messageId An arbitrary integer used by the caller to identify the message. The same * integer ID will be returned in the callbacks indicating message send success or @@ -174,22 +173,22 @@ public class WifiNanSession { /** * Sends a message to the specified destination. NAN messages are transmitted in the context * of a discovery session - executed subsequent to a publish/subscribe - * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} event. + * {@link WifiNanDiscoverySessionCallback#onMatch(int, byte[], byte[])} event. * <p> - * NAN messages are not guaranteed delivery. Callbacks on {@link WifiNanSessionCallback} - * indicate message was transmitted successfully, - * {@link WifiNanSessionCallback#onMessageSendSuccess(int)}, or transmission failed + * NAN messages are not guaranteed delivery. Callbacks on + * {@link WifiNanDiscoverySessionCallback} indicate message was transmitted successfully, + * {@link WifiNanDiscoverySessionCallback#onMessageSendSuccess(int)}, or transmission failed * (possibly after several retries) - - * {@link WifiNanSessionCallback#onMessageSendFail(int, int)}. + * {@link WifiNanDiscoverySessionCallback#onMessageSendFail(int, int)}. * <p> * The peer will get a callback indicating a message was received using - * {@link WifiNanSessionCallback#onMessageReceived(int, byte[])}. + * {@link WifiNanDiscoverySessionCallback#onMessageReceived(int, byte[])}. * Equivalent to {@link #sendMessage(int, byte[], int, int)} with a {@code retryCount} of * 0. * * @param peerId The peer's ID for the message. Must be a result of an - * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} or - * {@link WifiNanSessionCallback#onMessageReceived(int, byte[])} events. + * {@link WifiNanDiscoverySessionCallback#onMatch(int, byte[], byte[])} or + * {@link WifiNanDiscoverySessionCallback#onMessageReceived(int, byte[])} events. * @param message The message to be transmitted. * @param messageId An arbitrary integer used by the caller to identify the message. The same * integer ID will be returned in the callbacks indicating message send success or @@ -202,8 +201,8 @@ public class WifiNanSession { /** * Start a ranging operation with the specified peers. The peer IDs are obtained from an - * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} or - * {@link WifiNanSessionCallback#onMessageReceived(int, byte[])} operation - can only + * {@link WifiNanDiscoverySessionCallback#onMatch(int, byte[], byte[])} or + * {@link WifiNanDiscoverySessionCallback#onMessageReceived(int, byte[])} operation - can only * range devices which are part of an ongoing discovery session. * * @param params RTT parameters - each corresponding to a specific peer ID (the array sizes @@ -245,8 +244,8 @@ public class WifiNanSession { * {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_INITIATOR} or * {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_RESPONDER} * @param peerId The peer ID obtained through - * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} or - * {@link WifiNanSessionCallback#onMessageReceived(int, byte[])}. On a RESPONDER this + * {@link WifiNanDiscoverySessionCallback#onMatch(int, byte[], byte[])} or + * {@link WifiNanDiscoverySessionCallback#onMessageReceived(int, byte[])}. On a RESPONDER this * value is used to gate the acceptance of a connection request from only that * peer. A RESPONDER may specified a 0 - indicating that it will accept * connection requests from any device. diff --git a/wifi/java/android/net/wifi/nan/WifiNanSessionCallback.java b/wifi/java/android/net/wifi/nan/WifiNanDiscoverySessionCallback.java index 8433b99315ce..685e1524a27a 100644 --- a/wifi/java/android/net/wifi/nan/WifiNanSessionCallback.java +++ b/wifi/java/android/net/wifi/nan/WifiNanDiscoverySessionCallback.java @@ -26,14 +26,14 @@ import java.lang.annotation.RetentionPolicy; * Base class for NAN session events callbacks. Should be extended by * applications wanting notifications. The callbacks are set when a * publish or subscribe session is created using - * {@link WifiNanManager#publish(PublishConfig, WifiNanSessionCallback)} or - * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanSessionCallback)} . + * {@link WifiNanManager#publish(PublishConfig, WifiNanDiscoverySessionCallback)} or + * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanDiscoverySessionCallback)} . * <p> * A single callback is set at session creation - it cannot be replaced. * * @hide PROPOSED_NAN_API */ -public class WifiNanSessionCallback { +public class WifiNanDiscoverySessionCallback { /** @hide */ @IntDef({ REASON_NO_RESOURCES, REASON_INVALID_ARGS, REASON_NO_MATCH_SESSION, @@ -51,20 +51,20 @@ public class WifiNanSessionCallback { /** * Indicates no resources to execute the requested operation. - * Failure reason flag for {@link WifiNanSessionCallback} callbacks. + * Failure reason flag for {@link WifiNanDiscoverySessionCallback} callbacks. */ public static final int REASON_NO_RESOURCES = 0; /** * Indicates invalid argument in the requested operation. - * Failure reason flag for {@link WifiNanSessionCallback} callbacks. + * Failure reason flag for {@link WifiNanDiscoverySessionCallback} callbacks. */ public static final int REASON_INVALID_ARGS = 1; /** * Indicates a message is transmitted without a match (a discovery) or received message * from peer occurring first. - * Failure reason flag for {@link WifiNanSessionCallback} callbacks. + * Failure reason flag for {@link WifiNanDiscoverySessionCallback} callbacks. */ public static final int REASON_NO_MATCH_SESSION = 2; @@ -72,13 +72,13 @@ public class WifiNanSessionCallback { * Indicates transmission failure: this may be due to local transmission * failure or to no ACK received - remote device didn't receive the * sent message. Failure reason flag for - * {@link WifiNanSessionCallback#onMessageSendFail(int, int)} callback. + * {@link WifiNanDiscoverySessionCallback#onMessageSendFail(int, int)} callback. */ public static final int REASON_TX_FAIL = 3; /** * Indicates an unspecified error occurred during the operation. - * Failure reason flag for {@link WifiNanSessionCallback} callbacks. + * Failure reason flag for {@link WifiNanDiscoverySessionCallback} callbacks. */ public static final int REASON_OTHER = 4; @@ -86,7 +86,7 @@ public class WifiNanSessionCallback { * Indicates that publish or subscribe session is done - all the * requested operations (per {@link PublishConfig} or * {@link SubscribeConfig}) have been executed. Failure reason flag for - * {@link WifiNanSessionCallback#onSessionTerminated(int)} callback. + * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} callback. */ public static final int TERMINATE_REASON_DONE = 100; @@ -94,36 +94,37 @@ public class WifiNanSessionCallback { * Indicates that publish or subscribe session is terminated due to a * failure. * Failure reason flag for - * {@link WifiNanSessionCallback#onSessionTerminated(int)} callback. + * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} callback. */ public static final int TERMINATE_REASON_FAIL = 101; /** * Called when a publish operation is started successfully in response to a - * {@link WifiNanManager#publish(PublishConfig, WifiNanSessionCallback)} operation. + * {@link WifiNanManager#publish(PublishConfig, WifiNanDiscoverySessionCallback)} operation. * - * @param session The {@link WifiNanPublishSession} used to control the + * @param session The {@link WifiNanPublishDiscoverySession} used to control the * discovery session. */ - public void onPublishStarted(@NonNull WifiNanPublishSession session) { + public void onPublishStarted(@NonNull WifiNanPublishDiscoverySession session) { /* empty */ } /** * Called when a subscribe operation is started successfully in response to a - * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanSessionCallback)} operation. + * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanDiscoverySessionCallback)} operation. * - * @param session The {@link WifiNanSubscribeSession} used to control the + * @param session The {@link WifiNanSubscribeDiscoverySession} used to control the * discovery session. */ - public void onSubscribeStarted(@NonNull WifiNanSubscribeSession session) { + public void onSubscribeStarted(@NonNull WifiNanSubscribeDiscoverySession session) { /* empty */ } /** * Called when a publish or subscribe discovery session configuration is update request - * succeeds. Called in response to {@link WifiNanPublishSession#updatePublish(PublishConfig)} - * or {@link WifiNanSubscribeSession#updateSubscribe(SubscribeConfig)}. + * succeeds. Called in response to + * {@link WifiNanPublishDiscoverySession#updatePublish(PublishConfig)} or + * {@link WifiNanSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. */ public void onSessionConfigSuccess() { /* empty */ @@ -131,17 +132,17 @@ public class WifiNanSessionCallback { /** * Called when a publish or subscribe discovery session cannot be created: - * {@link WifiNanManager#publish(PublishConfig, WifiNanSessionCallback)} or - * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanSessionCallback)}, + * {@link WifiNanManager#publish(PublishConfig, WifiNanDiscoverySessionCallback)} or + * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanDiscoverySessionCallback)}, * or when a configuration update fails: - * {@link WifiNanPublishSession#updatePublish(PublishConfig)} or - * {@link WifiNanSubscribeSession#updateSubscribe(SubscribeConfig)}. + * {@link WifiNanPublishDiscoverySession#updatePublish(PublishConfig)} or + * {@link WifiNanSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. * <p> * For discovery session updates failure leaves the session running with its previous * configuration - the discovery session is not terminated. * * @param reason The failure reason using - * {@code WifiNanSessionCallback.REASON_*} codes. + * {@code WifiNanDiscoverySessionCallback.REASON_*} codes. */ public void onSessionConfigFail(@SessionReasonCodes int reason) { /* empty */ @@ -149,12 +150,12 @@ public class WifiNanSessionCallback { /** * Called when a discovery session (publish or subscribe) terminates. Termination may be due - * to user-request (either directly through {@link WifiNanSession#terminate()} or + * to user-request (either directly through {@link WifiNanDiscoveryBaseSession#terminate()} or * application-specified expiration, e.g. {@link PublishConfig.Builder#setPublishCount(int)} * or {@link SubscribeConfig.Builder#setTtlSec(int)}) or due to a failure. * * @param reason The termination reason using - * {@code WifiNanSessionCallback.TERMINATE_*} codes. + * {@code WifiNanDiscoverySessionCallback.TERMINATE_*} codes. */ public void onSessionTerminated(@SessionTerminateCodes int reason) { /* empty */ @@ -176,12 +177,12 @@ public class WifiNanSessionCallback { } /** - * Called in response to {@link WifiNanSession#sendMessage(int, byte[], int)} when a message - * is transmitted successfully - when it was received successfully by the peer + * Called in response to {@link WifiNanDiscoveryBaseSession#sendMessage(int, byte[], int)} + * when a message is transmitted successfully - when it was received successfully by the peer * (corresponds to an ACK being received). * <p> * Note that either this callback or - * {@link WifiNanSessionCallback#onMessageSendFail(int, int)} will be + * {@link WifiNanDiscoverySessionCallback#onMessageSendFail(int, int)} will be * received - never both. * * @param messageId The arbitrary message ID specified when sending the message. @@ -193,16 +194,16 @@ public class WifiNanSessionCallback { /** * Called when message transmission fails - when no ACK is received from the peer. * Retries when ACKs are not received are done by hardware, MAC, and in the NAN stack (using - * the {@link WifiNanSession#sendMessage(int, byte[], int, int)} method) - this + * the {@link WifiNanDiscoveryBaseSession#sendMessage(int, byte[], int, int)} method) - this * event is received after all retries are exhausted. * <p> * Note that either this callback or - * {@link WifiNanSessionCallback#onMessageSendSuccess(int)} will be received + * {@link WifiNanDiscoverySessionCallback#onMessageSendSuccess(int)} will be received * - never both. * * @param messageId The arbitrary message ID specified when sending the message. * @param reason The failure reason using - * {@code WifiNanSessionCallback.REASON_*} codes. + * {@code WifiNanDiscoverySessionCallback.REASON_*} codes. */ public void onMessageSendFail(@SuppressWarnings("unused") int messageId, @SessionReasonCodes int reason) { @@ -211,8 +212,8 @@ public class WifiNanSessionCallback { /** * Called when a message is received from a discovery session peer - in response to the - * peer's {@link WifiNanSession#sendMessage(int, byte[], int)} or - * {@link WifiNanSession#sendMessage(int, byte[], int, int)}. + * peer's {@link WifiNanDiscoveryBaseSession#sendMessage(int, byte[], int)} or + * {@link WifiNanDiscoveryBaseSession#sendMessage(int, byte[], int, int)}. * * @param peerId The ID of the peer sending the message. * @param message A byte array containing the message. diff --git a/wifi/java/android/net/wifi/nan/WifiNanEventCallback.java b/wifi/java/android/net/wifi/nan/WifiNanEventCallback.java index 029e36a41be6..a66afc189c4c 100644 --- a/wifi/java/android/net/wifi/nan/WifiNanEventCallback.java +++ b/wifi/java/android/net/wifi/nan/WifiNanEventCallback.java @@ -25,7 +25,7 @@ import java.lang.annotation.RetentionPolicy; * Base class for NAN events callbacks. Should be extended by applications and set when calling * {@link WifiNanManager#connect(android.os.Handler, WifiNanEventCallback)}. These are callbacks * applying to the NAN connection as a whole - not to specific publish or subscribe sessions - - * for that see {@link WifiNanSessionCallback}. + * for that see {@link WifiNanDiscoverySessionCallback}. * * @hide PROPOSED_NAN_API */ diff --git a/wifi/java/android/net/wifi/nan/WifiNanManager.java b/wifi/java/android/net/wifi/nan/WifiNanManager.java index 24ee640a63df..6eb476d229ad 100644 --- a/wifi/java/android/net/wifi/nan/WifiNanManager.java +++ b/wifi/java/android/net/wifi/nan/WifiNanManager.java @@ -21,7 +21,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.SystemApi; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkRequest; @@ -62,12 +61,12 @@ import java.util.Arrays; * <li>Initialize a NAN cluster (peer-to-peer synchronization). Refer to * {@link #connect(Handler, WifiNanEventCallback)}. * <li>Create discovery sessions (publish or subscribe sessions). - * Refer to {@link #publish(PublishConfig, WifiNanSessionCallback)} and - * {@link #subscribe(SubscribeConfig, WifiNanSessionCallback)}. + * Refer to {@link #publish(PublishConfig, WifiNanDiscoverySessionCallback)} and + * {@link #subscribe(SubscribeConfig, WifiNanDiscoverySessionCallback)}. * <li>Create a NAN network specifier to be used with * {@link ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)} * to set-up a NAN connection with a peer. Refer to - * {@link WifiNanSession#createNetworkSpecifier(int, int, byte[])} and + * {@link WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, int, byte[])} and * {@link #createNetworkSpecifier(int, byte[], byte[])}. * </ul> * <p> @@ -89,19 +88,21 @@ import java.util.Arrays; * device will actually disconnect from the NAN cluster once the last application disconnects. * <p> * Once a NAN connection is confirmed use the - * {@link #publish(PublishConfig, WifiNanSessionCallback)} or - * {@link #subscribe(SubscribeConfig, WifiNanSessionCallback)} to create publish or subscribe - * NAN discovery sessions. Events are called on the provided callback object - * {@link WifiNanSessionCallback}. Specifically, the - * {@link WifiNanSessionCallback#onPublishStarted(WifiNanPublishSession)} and - * {@link WifiNanSessionCallback#onSubscribeStarted(WifiNanSubscribeSession)} return - * {@link WifiNanPublishSession} and {@link WifiNanSubscribeSession} objects respectively on - * which additional session operations can be performed, e.g. updating the session - * {@link WifiNanPublishSession#updatePublish(PublishConfig)} and - * {@link WifiNanSubscribeSession#updateSubscribe(SubscribeConfig)}. Sessions can also be - * used to send messages using the {@link WifiNanSession#sendMessage(int, byte[], int)} APIs. - * When an application is finished with a discovery session it <b>must</b> terminate it using - * the {@link WifiNanSession#terminate()} API. + * {@link #publish(PublishConfig, WifiNanDiscoverySessionCallback)} or + * {@link #subscribe(SubscribeConfig, WifiNanDiscoverySessionCallback)} to create publish or + * subscribe NAN discovery sessions. Events are called on the provided callback object + * {@link WifiNanDiscoverySessionCallback}. Specifically, the + * {@link WifiNanDiscoverySessionCallback#onPublishStarted(WifiNanPublishDiscoverySession)} + * and + * {@link WifiNanDiscoverySessionCallback#onSubscribeStarted(WifiNanSubscribeDiscoverySession)} + * return {@link WifiNanPublishDiscoverySession} and {@link WifiNanSubscribeDiscoverySession} + * objects respectively on which additional session operations can be performed, e.g. updating + * the session {@link WifiNanPublishDiscoverySession#updatePublish(PublishConfig)} and + * {@link WifiNanSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. Sessions can also + * be used to send messages using the + * {@link WifiNanDiscoveryBaseSession#sendMessage(int, byte[], int)} APIs. When an application + * is finished with a discovery session it <b>must</b> terminate it using the + * {@link WifiNanDiscoveryBaseSession#terminate()} API. * <p> * Creating connections between NAN devices is managed by the standard * {@link ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)}. @@ -111,7 +112,7 @@ import java.util.Arrays; * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_NAN}. * <li>{@link NetworkRequest.Builder#setNetworkSpecifier(String)} using * {@link #createNetworkSpecifier(int, byte[], byte[])} or - * {@link WifiNanSession#createNetworkSpecifier(int, int, byte[])}. + * {@link WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, int, byte[])}. * </ul> * * @hide PROPOSED_NAN_API @@ -249,7 +250,7 @@ public class WifiNanManager { * Connection creation role is that of INITIATOR. Used to create a network specifier string * when requesting a NAN network. * - * @see WifiNanSession#createNetworkSpecifier(int, int, byte[]) + * @see WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, int, byte[]) * @see #createNetworkSpecifier(int, byte[], byte[]) */ public static final int WIFI_NAN_DATA_PATH_ROLE_INITIATOR = 0; @@ -258,7 +259,7 @@ public class WifiNanManager { * Connection creation role is that of RESPONDER. Used to create a network specifier string * when requesting a NAN network. * - * @see WifiNanSession#createNetworkSpecifier(int, int, byte[]) + * @see WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, int, byte[]) * @see #createNetworkSpecifier(int, byte[], byte[]) */ public static final int WIFI_NAN_DATA_PATH_ROLE_RESPONDER = 1; @@ -445,30 +446,30 @@ public class WifiNanManager { /** * Issue a request to the NAN service to create a new NAN publish discovery session, using * the specified {@code publishConfig} configuration. The results of the publish operation - * are routed to the callbacks of {@link WifiNanSessionCallback}: + * are routed to the callbacks of {@link WifiNanDiscoverySessionCallback}: * <ul> - * <li>{@link WifiNanSessionCallback#onPublishStarted(WifiNanPublishSession)} is called - * when the publish session is created and provides a handle to the session. Further - * operations on the publish session can be executed on that object. - * <li>{@link WifiNanSessionCallback#onSessionConfigFail(int)} is called if the publish - * operation failed. + * <li>{@link WifiNanDiscoverySessionCallback#onPublishStarted(WifiNanPublishDiscoverySession)} + * is called when the publish session is created and provides a handle to the session. + * Further operations on the publish session can be executed on that object. + * <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigFail(int)} is called if the + * publish operation failed. * </ul> * <p> * Other results of the publish session operations will also be routed to callbacks * on the {@code callback} object. The resulting publish session can be modified using - * {@link WifiNanPublishSession#updatePublish(PublishConfig)}. + * {@link WifiNanPublishDiscoverySession#updatePublish(PublishConfig)}. * <p> - * An application must use the {@link WifiNanSession#terminate()} to terminate the publish - * discovery session once it isn't needed. This will free resources as well terminate - * any on-air transmissions. + * An application must use the {@link WifiNanDiscoveryBaseSession#terminate()} to + * terminate the publish discovery session once it isn't needed. This will free + * resources as well terminate any on-air transmissions. * * @param publishConfig The {@link PublishConfig} specifying the * configuration of the requested publish session. - * @param callback A {@link WifiNanSessionCallback} derived object to be used for session - * event callbacks. + * @param callback A {@link WifiNanDiscoverySessionCallback} derived object to be used for + * session event callbacks. */ public void publish(@NonNull PublishConfig publishConfig, - @NonNull WifiNanSessionCallback callback) { + @NonNull WifiNanDiscoverySessionCallback callback) { if (VDBG) Log.v(TAG, "publish(): config=" + publishConfig); int clientId; @@ -485,7 +486,7 @@ public class WifiNanManager { } try { mService.publish(clientId, publishConfig, - new WifiNanSessionCallbackProxy(this, looper, true, callback)); + new WifiNanDiscoverySessionCallbackProxy(this, looper, true, callback)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -514,30 +515,30 @@ public class WifiNanManager { /** * Issue a request to the NAN service to create a new NAN subscribe discovery session, using * the specified {@code subscribeConfig} configuration. The results of the subscribe - * operation are routed to the callbacks of {@link WifiNanSessionCallback}: + * operation are routed to the callbacks of {@link WifiNanDiscoverySessionCallback}: * <ul> - * <li>{@link WifiNanSessionCallback#onSubscribeStarted(WifiNanSubscribeSession)} is called - * when the subscribe session is created and provides a handle to the session. Further - * operations on the subscribe session can be executed on that object. - * <li>{@link WifiNanSessionCallback#onSessionConfigFail(int)} is called if the subscribe - * operation failed. + * <li>{@link WifiNanDiscoverySessionCallback#onSubscribeStarted(WifiNanSubscribeDiscoverySession)} + * is called when the subscribe session is created and provides a handle to the session. + * Further operations on the subscribe session can be executed on that object. + * <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigFail(int)} is called if the + * subscribe operation failed. * </ul> * <p> * Other results of the subscribe session operations will also be routed to callbacks * on the {@code callback} object. The resulting subscribe session can be modified using - * {@link WifiNanSubscribeSession#updateSubscribe(SubscribeConfig)}. + * {@link WifiNanSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. * <p> - * An application must use the {@link WifiNanSession#terminate()} to terminate the - * subscribe discovery session once it isn't needed. This will free resources as well - * terminate any on-air transmissions. + * An application must use the {@link WifiNanDiscoveryBaseSession#terminate()} to + * terminate the subscribe discovery session once it isn't needed. This will free + * resources as well terminate any on-air transmissions. * * @param subscribeConfig The {@link SubscribeConfig} specifying the * configuration of the requested subscribe session. - * @param callback A {@link WifiNanSessionCallback} derived object to be used for session - * event callbacks. + * @param callback A {@link WifiNanDiscoverySessionCallback} derived object to be used for + * session event callbacks. */ public void subscribe(@NonNull SubscribeConfig subscribeConfig, - @NonNull WifiNanSessionCallback callback) { + @NonNull WifiNanDiscoverySessionCallback callback) { if (VDBG) { Log.v(TAG, "subscribe(): config=" + subscribeConfig); } @@ -557,7 +558,7 @@ public class WifiNanManager { try { mService.subscribe(clientId, subscribeConfig, - new WifiNanSessionCallbackProxy(this, looper, false, callback)); + new WifiNanDiscoverySessionCallbackProxy(this, looper, false, callback)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -744,7 +745,7 @@ public class WifiNanManager { * This API is targeted for applications which can obtain the peer MAC address using OOB * (out-of-band) discovery. NAN discovery does not provide the MAC address of the peer - * when using NAN discovery use the alternative network specifier method - - * {@link WifiNanSession#createNetworkSpecifier(int, int, byte[])}. + * {@link WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, int, byte[])}. * * @param role The role of this device: * {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_INITIATOR} or @@ -760,8 +761,8 @@ public class WifiNanManager { * not the same as a null token and requires the peer token to be empty as well. * * @return A string to be used to construct - * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to {@link - * android.net.ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)} + * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to + * {@link android.net.ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)} * [or other varieties of that API]. */ public String createNetworkSpecifier(@DataPathRole int role, @Nullable byte[] peer, @@ -999,7 +1000,8 @@ public class WifiNanManager { } } - private static class WifiNanSessionCallbackProxy extends IWifiNanSessionCallback.Stub { + private static class WifiNanDiscoverySessionCallbackProxy extends + IWifiNanDiscoverySessionCallback.Stub { private static final int CALLBACK_SESSION_STARTED = 0; private static final int CALLBACK_SESSION_CONFIG_SUCCESS = 1; private static final int CALLBACK_SESSION_CONFIG_FAIL = 2; @@ -1014,18 +1016,20 @@ public class WifiNanManager { private final WeakReference<WifiNanManager> mNanManager; private final boolean mIsPublish; - private final WifiNanSessionCallback mOriginalCallback; + private final WifiNanDiscoverySessionCallback mOriginalCallback; private final Handler mHandler; - private WifiNanSession mSession; + private WifiNanDiscoveryBaseSession mSession; - WifiNanSessionCallbackProxy(WifiNanManager mgr, Looper looper, boolean isPublish, - WifiNanSessionCallback originalCallback) { + WifiNanDiscoverySessionCallbackProxy(WifiNanManager mgr, Looper looper, boolean isPublish, + WifiNanDiscoverySessionCallback originalCallback) { mNanManager = new WeakReference<>(mgr); mIsPublish = isPublish; mOriginalCallback = originalCallback; - if (VDBG) Log.v(TAG, "WifiNanSessionCallbackProxy ctor: isPublish=" + isPublish); + if (VDBG) { + Log.v(TAG, "WifiNanDiscoverySessionCallbackProxy ctor: isPublish=" + isPublish); + } mHandler = new Handler(looper) { @Override @@ -1033,7 +1037,7 @@ public class WifiNanManager { if (DBG) Log.d(TAG, "What=" + msg.what + ", msg=" + msg); if (mNanManager.get() == null) { - Log.w(TAG, "WifiNanSessionCallbackProxy: handleMessage post GC"); + Log.w(TAG, "WifiNanDiscoverySessionCallbackProxy: handleMessage post GC"); return; } @@ -1176,11 +1180,13 @@ public class WifiNanManager { } if (mIsPublish) { - WifiNanPublishSession session = new WifiNanPublishSession(mgr, sessionId); + WifiNanPublishDiscoverySession session = new WifiNanPublishDiscoverySession(mgr, + sessionId); mSession = session; mOriginalCallback.onPublishStarted(session); } else { - WifiNanSubscribeSession session = new WifiNanSubscribeSession(mgr, sessionId); + WifiNanSubscribeDiscoverySession + session = new WifiNanSubscribeDiscoverySession(mgr, sessionId); mSession = session; mOriginalCallback.onSubscribeStarted(session); } diff --git a/wifi/java/android/net/wifi/nan/WifiNanPublishSession.java b/wifi/java/android/net/wifi/nan/WifiNanPublishDiscoverySession.java index ccd7faee8476..cacbdbf751d8 100644 --- a/wifi/java/android/net/wifi/nan/WifiNanPublishSession.java +++ b/wifi/java/android/net/wifi/nan/WifiNanPublishDiscoverySession.java @@ -21,32 +21,33 @@ import android.util.Log; /** * A class representing a NAN publish session. Created when - * {@link WifiNanManager#publish(PublishConfig, WifiNanSessionCallback)} is called and a + * {@link WifiNanManager#publish(PublishConfig, WifiNanDiscoverySessionCallback)} is called and a * discovery session is created and returned in - * {@link WifiNanSessionCallback#onPublishStarted(WifiNanPublishSession)}. See baseline - * functionality of all discovery sessions in {@link WifiNanSession}. This object allows updating - * an existing/running publish discovery session using {@link #updatePublish(PublishConfig)}. + * {@link WifiNanDiscoverySessionCallback#onPublishStarted(WifiNanPublishDiscoverySession)}. See + * baseline functionality of all discovery sessions in {@link WifiNanDiscoveryBaseSession}. This + * object allows updating an existing/running publish discovery session using + * {@link #updatePublish(PublishConfig)}. * * @hide PROPOSED_NAN_API */ -public class WifiNanPublishSession extends WifiNanSession { - private static final String TAG = "WifiNanPublishSession"; +public class WifiNanPublishDiscoverySession extends WifiNanDiscoveryBaseSession { + private static final String TAG = "WifiNanPublishDiscSsn"; /** @hide */ - public WifiNanPublishSession(WifiNanManager manager, int sessionId) { + public WifiNanPublishDiscoverySession(WifiNanManager manager, int sessionId) { super(manager, sessionId); } /** * Re-configure the currently active publish session. The - * {@link WifiNanSessionCallback} is not replaced - the same listener used + * {@link WifiNanDiscoverySessionCallback} is not replaced - the same listener used * at creation is still used. The results of the configuration are returned using - * {@link WifiNanSessionCallback}: + * {@link WifiNanDiscoverySessionCallback}: * <ul> - * <li>{@link WifiNanSessionCallback#onSessionConfigSuccess()}: configuration update - * succeeded. - * <li>{@link WifiNanSessionCallback#onSessionConfigFail(int)}: configuration update - * failed. The publish discovery session is still running using its previous + * <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigSuccess()}: configuration + * update succeeded. + * <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigFail(int)}: configuration + * update failed. The publish discovery session is still running using its previous * configuration (i.e. update failure does not terminate the session). * </ul> * diff --git a/wifi/java/android/net/wifi/nan/WifiNanSubscribeSession.java b/wifi/java/android/net/wifi/nan/WifiNanSubscribeDiscoverySession.java index d0e56c521525..58c52d5faf72 100644 --- a/wifi/java/android/net/wifi/nan/WifiNanSubscribeSession.java +++ b/wifi/java/android/net/wifi/nan/WifiNanSubscribeDiscoverySession.java @@ -21,34 +21,35 @@ import android.util.Log; /** * A class representing a NAN subscribe session. Created when - * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanSessionCallback)} is called and a - * discovery session is created and returned in - * {@link WifiNanSessionCallback#onSubscribeStarted(WifiNanSubscribeSession)}. See baseline - * functionality of all discovery sessions in {@link WifiNanSession}. This object allows updating - * an existing/running subscribe discovery session using {@link #updateSubscribe(SubscribeConfig)}. + * {@link WifiNanManager#subscribe(SubscribeConfig, WifiNanDiscoverySessionCallback)} is called + * and a discovery session is created and returned in + * {@link WifiNanDiscoverySessionCallback#onSubscribeStarted(WifiNanSubscribeDiscoverySession)}. + * See baseline functionality of all discovery sessions in {@link WifiNanDiscoveryBaseSession}. + * This object allows updating an existing/running subscribe discovery session using + * {@link #updateSubscribe(SubscribeConfig)}. * * @hide PROPOSED_NAN_API */ -public class WifiNanSubscribeSession extends WifiNanSession { - private static final String TAG = "WifiNanSubscribeSession"; +public class WifiNanSubscribeDiscoverySession extends WifiNanDiscoveryBaseSession { + private static final String TAG = "WifiNanSubscribeDiscSsn"; /** * {@hide} */ - public WifiNanSubscribeSession(WifiNanManager manager, int sessionId) { + public WifiNanSubscribeDiscoverySession(WifiNanManager manager, int sessionId) { super(manager, sessionId); } /** * Re-configure the currently active subscribe session. The - * {@link WifiNanSessionCallback} is not replaced - the same listener used + * {@link WifiNanDiscoverySessionCallback} is not replaced - the same listener used * at creation is still used. The results of the configuration are returned using - * {@link WifiNanSessionCallback}: + * {@link WifiNanDiscoverySessionCallback}: * <ul> - * <li>{@link WifiNanSessionCallback#onSessionConfigSuccess()}: configuration update - * succeeded. - * <li>{@link WifiNanSessionCallback#onSessionConfigFail(int)}: configuration update - * failed. The subscribe discovery session is still running using its previous + * <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigSuccess()}: configuration + * update succeeded. + * <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigFail(int)}: configuration + * update failed. The subscribe discovery session is still running using its previous * configuration (i.e. update failure does not terminate the session). * </ul> * |