diff options
224 files changed, 6298 insertions, 718 deletions
diff --git a/Android.mk b/Android.mk index 6631adace544..313f329f50af 100644 --- a/Android.mk +++ b/Android.mk @@ -566,6 +566,15 @@ LOCAL_SRC_FILES += \ LOCAL_AIDL_INCLUDES += system/update_engine/binder_bindings +LOCAL_AIDL_INCLUDES += frameworks/base/lowpan/java +LOCAL_SRC_FILES += \ + lowpan/java/android/net/lowpan/ILowpanEnergyScanCallback.aidl \ + lowpan/java/android/net/lowpan/ILowpanNetScanCallback.aidl \ + lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl \ + lowpan/java/android/net/lowpan/ILowpanInterface.aidl \ + lowpan/java/android/net/lowpan/ILowpanManagerListener.aidl \ + lowpan/java/android/net/lowpan/ILowpanManager.aidl + # FRAMEWORKS_BASE_JAVA_SRC_DIRS comes from build/core/pathmap.mk LOCAL_AIDL_INCLUDES += \ $(FRAMEWORKS_BASE_JAVA_SRC_DIRS) \ @@ -967,6 +976,7 @@ framework_docs_LOCAL_DROIDDOC_OPTIONS := \ -android \ -knowntags ./frameworks/base/docs/knowntags.txt \ -knowntags ./libcore/known_oj_tags.txt \ + -manifest ./frameworks/base/core/res/AndroidManifest.xml \ -hidePackage com.android.org.conscrypt \ -since $(SRC_API_DIR)/1.xml 1 \ -since $(SRC_API_DIR)/2.xml 2 \ diff --git a/api/current.txt b/api/current.txt index c4ddfaf359c6..a71c63ae598c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -43936,6 +43936,7 @@ package android.view { field public static final int KEYBOARD_TAP = 3; // 0x3 field public static final int LONG_PRESS = 0; // 0x0 field public static final int VIRTUAL_KEY = 1; // 0x1 + field public static final int VIRTUAL_KEY_RELEASE = 7; // 0x7 } public class InflateException extends java.lang.RuntimeException { diff --git a/api/system-current.txt b/api/system-current.txt index f0f019a12219..fbe210925ab3 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -47483,6 +47483,7 @@ package android.view { field public static final int KEYBOARD_TAP = 3; // 0x3 field public static final int LONG_PRESS = 0; // 0x0 field public static final int VIRTUAL_KEY = 1; // 0x1 + field public static final int VIRTUAL_KEY_RELEASE = 7; // 0x7 } public class InflateException extends java.lang.RuntimeException { diff --git a/api/test-current.txt b/api/test-current.txt index 201d9b3f6ddf..d586670b53f9 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -44319,6 +44319,7 @@ package android.view { field public static final int KEYBOARD_TAP = 3; // 0x3 field public static final int LONG_PRESS = 0; // 0x0 field public static final int VIRTUAL_KEY = 1; // 0x1 + field public static final int VIRTUAL_KEY_RELEASE = 7; // 0x7 } public class InflateException extends java.lang.RuntimeException { diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index 8fd8043a772a..a446296fe393 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -24,6 +24,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.Size; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.BroadcastBehavior; import android.app.Activity; @@ -161,6 +162,7 @@ import java.util.concurrent.TimeoutException; * the application's main event thread. These operations throw * {@link IllegalStateException} if they are used on the main thread. */ +@SystemService(Context.ACCOUNT_SERVICE) public class AccountManager { private static final String TAG = "AccountManager"; @@ -2509,6 +2511,18 @@ public class AccountManager { return new AuthenticatorException(message); } + private void getAccountByTypeAndFeatures(String accountType, String[] features, + AccountManagerCallback<Bundle> callback, Handler handler) { + (new AmsTask(null, handler, callback) { + @Override + public void doWork() throws RemoteException { + mService.getAccountByTypeAndFeatures(mResponse, accountType, features, + mContext.getOpPackageName()); + } + + }).start(); + } + private class GetAuthTokenByTypeAndFeaturesTask extends AmsTask implements AccountManagerCallback<Bundle> { GetAuthTokenByTypeAndFeaturesTask(final String accountType, final String authTokenType, @@ -2535,13 +2549,16 @@ public class AccountManager { @Override public void doWork() throws RemoteException { - getAccountsByTypeAndFeatures(mAccountType, mFeatures, - new AccountManagerCallback<Account[]>() { + getAccountByTypeAndFeatures(mAccountType, mFeatures, + new AccountManagerCallback<Bundle>() { @Override - public void run(AccountManagerFuture<Account[]> future) { - Account[] accounts; + public void run(AccountManagerFuture<Bundle> future) { + String accountName = null; + String accountType = null; try { - accounts = future.getResult(); + Bundle result = future.getResult(); + accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME); + accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE); } catch (OperationCanceledException e) { setException(e); return; @@ -2553,9 +2570,7 @@ public class AccountManager { return; } - mNumAccounts = accounts.length; - - if (accounts.length == 0) { + if (accountName == null) { if (mActivity != null) { // no accounts, add one now. pretend that the user directly // made this request @@ -2575,63 +2590,17 @@ public class AccountManager { } // we are done } - } else if (accounts.length == 1) { + } else { + mNumAccounts = 1; + Account account = new Account(accountName, accountType); // have a single account, return an authtoken for it if (mActivity == null) { - mFuture = getAuthToken(accounts[0], mAuthTokenType, + mFuture = getAuthToken(account, mAuthTokenType, false /* notifyAuthFailure */, mMyCallback, mHandler); } else { - mFuture = getAuthToken(accounts[0], - mAuthTokenType, mLoginOptions, + mFuture = getAuthToken(account, mAuthTokenType, mLoginOptions, mActivity, mMyCallback, mHandler); } - } else { - if (mActivity != null) { - IAccountManagerResponse chooseResponse = - new IAccountManagerResponse.Stub() { - @Override - public void onResult(Bundle value) throws RemoteException { - Account account = new Account( - value.getString(KEY_ACCOUNT_NAME), - value.getString(KEY_ACCOUNT_TYPE), - value.getString(KEY_ACCOUNT_ACCESS_ID)); - mFuture = getAuthToken(account, mAuthTokenType, - mLoginOptions, mActivity, mMyCallback, - mHandler); - } - - @Override - public void onError(int errorCode, String errorMessage) - throws RemoteException { - mResponse.onError(errorCode, errorMessage); - } - }; - // have many accounts, launch the chooser - Intent intent = new Intent(); - // TODO - this activity will not include - // USER_MANAGED_NOT_VISIBLE - // accounts. We need to move method to service - ComponentName componentName = ComponentName.unflattenFromString( - Resources.getSystem().getString( - R.string.config_chooseAccountActivity)); - intent.setClassName(componentName.getPackageName(), - componentName.getClassName()); - intent.putExtra(KEY_ACCOUNTS, accounts); - intent.putExtra(KEY_ACCOUNT_MANAGER_RESPONSE, - new AccountManagerResponse(chooseResponse)); - mActivity.startActivity(intent); - // the result will arrive via the IAccountManagerResponse - } else { - // send result since we can't prompt to select an account - Bundle result = new Bundle(); - result.putString(KEY_ACCOUNTS, null); - try { - mResponse.onResult(result); - } catch (RemoteException e) { - // this will never happen - } - // we are done - } } }}, mHandler); } @@ -2721,8 +2690,8 @@ public class AccountManager { public AccountManagerFuture<Bundle> getAuthTokenByFeatures( final String accountType, final String authTokenType, final String[] features, final Activity activity, final Bundle addAccountOptions, - final Bundle getAuthTokenOptions, - final AccountManagerCallback<Bundle> callback, final Handler handler) { + final Bundle getAuthTokenOptions, final AccountManagerCallback<Bundle> callback, + final Handler handler) { if (accountType == null) throw new IllegalArgumentException("account type is null"); if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); final GetAuthTokenByTypeAndFeaturesTask task = @@ -3237,6 +3206,7 @@ public class AccountManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public AccountManagerFuture<Bundle> finishSessionAsUser( final Bundle sessionBundle, final Activity activity, diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl index 7494cfc383b4..4cf0a2089fe5 100644 --- a/core/java/android/accounts/IAccountManager.aidl +++ b/core/java/android/accounts/IAccountManager.aidl @@ -40,6 +40,8 @@ interface IAccountManager { Account[] getAccountsAsUser(String accountType, int userId, String opPackageName); void hasFeatures(in IAccountManagerResponse response, in Account account, in String[] features, String opPackageName); + void getAccountByTypeAndFeatures(in IAccountManagerResponse response, String accountType, + in String[] features, String opPackageName); void getAccountsByFeatures(in IAccountManagerResponse response, String accountType, in String[] features, String opPackageName); boolean addAccountExplicitly(in Account account, String password, in Bundle extras); diff --git a/core/java/android/annotation/SystemService.java b/core/java/android/annotation/SystemService.java new file mode 100644 index 000000000000..ba5002a4f1b5 --- /dev/null +++ b/core/java/android/annotation/SystemService.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017 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.annotation; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.content.Context; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Description of a system service available through + * {@link Context#getSystemService(Class)}. + * + * @hide + */ +@Retention(SOURCE) +@Target(TYPE) +public @interface SystemService { + String value(); +} diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index dec5f4ffc439..199e856e7a22 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.annotation.TestApi; import android.content.pm.ActivityInfo; import android.content.res.Configuration; @@ -117,6 +118,7 @@ import java.util.List; * be used for testing and debugging purposes only. * </p> */ +@SystemService(Context.ACTIVITY_SERVICE) public class ActivityManager { private static String TAG = "ActivityManager"; @@ -3626,6 +3628,7 @@ public class ActivityManager { * @hide */ @SystemApi @TestApi + @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS) public void addOnUidImportanceListener(OnUidImportanceListener listener, @RunningAppProcessInfo.Importance int importanceCutpoint) { synchronized (this) { @@ -3654,6 +3657,7 @@ public class ActivityManager { * @hide */ @SystemApi @TestApi + @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(OnUidImportanceListener listener) { synchronized (this) { UidObserver observer = mImportanceListeners.remove(listener); @@ -4021,6 +4025,10 @@ public class ActivityManager { * @hide */ @SystemApi + @RequiresPermission(anyOf = { + "android.permission.INTERACT_ACROSS_USERS", + "android.permission.INTERACT_ACROSS_USERS_FULL" + }) public static int getCurrentUser() { UserInfo ui; try { diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java index 620e5cf374ca..2813e8b9707e 100644 --- a/core/java/android/app/AlarmManager.java +++ b/core/java/android/app/AlarmManager.java @@ -17,8 +17,10 @@ package android.app; import android.annotation.IntDef; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.content.Context; import android.content.Intent; import android.os.Build; @@ -72,12 +74,8 @@ import java.lang.annotation.RetentionPolicy; * {@link #setExact(int, long, PendingIntent)}. Applications whose {@code targetSdkVersion} * is earlier than API 19 will continue to see the previous behavior in which all * alarms are delivered exactly when requested. - * - * <p>You do not - * instantiate this class directly; instead, retrieve it through - * {@link android.content.Context#getSystemService - * Context.getSystemService(Context.ALARM_SERVICE)}. */ +@SystemService(Context.ALARM_SERVICE) public class AlarmManager { private static final String TAG = "AlarmManager"; @@ -599,6 +597,7 @@ public class AlarmManager { /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void set(@AlarmType int type, long triggerAtMillis, long windowMillis, long intervalMillis, PendingIntent operation, WorkSource workSource) { setImpl(type, triggerAtMillis, windowMillis, intervalMillis, 0, operation, null, null, @@ -633,6 +632,7 @@ public class AlarmManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void set(@AlarmType int type, long triggerAtMillis, long windowMillis, long intervalMillis, OnAlarmListener listener, Handler targetHandler, WorkSource workSource) { diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 82921524457f..e672ada3cbb4 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -17,7 +17,9 @@ package android.app; import android.Manifest; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.app.usage.UsageStatsManager; import android.content.Context; import android.media.AudioAttributes.AttributeUsage; @@ -42,10 +44,9 @@ import java.util.List; * API for interacting with "application operation" tracking. * * <p>This API is not generally intended for third party application developers; most - * features are only available to system applications. Obtain an instance of it through - * {@link Context#getSystemService(String) Context.getSystemService} with - * {@link Context#APP_OPS_SERVICE Context.APP_OPS_SERVICE}.</p> + * features are only available to system applications. */ +@SystemService(Context.APP_OPS_SERVICE) public class AppOpsManager { /** * <p>App ops allows callers to:</p> @@ -1409,6 +1410,7 @@ public class AppOpsManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS) public void setUidMode(String appOp, int uid, int mode) { try { mService.setUidMode(AppOpsManager.strOpToOp(appOp), uid, mode); diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index 175b9799c5db..b6cff385d752 100644 --- a/core/java/android/app/BroadcastOptions.java +++ b/core/java/android/app/BroadcastOptions.java @@ -16,6 +16,7 @@ package android.app; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.os.Build; import android.os.Bundle; @@ -72,6 +73,7 @@ public class BroadcastOptions { * power whitelist when this broadcast is being delivered to it. * @param duration The duration in milliseconds; 0 means to not place on whitelist. */ + @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void setTemporaryAppWhitelistDuration(long duration) { mTemporaryAppWhitelistDuration = duration; } diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java index b89c16539b4c..5baaeb30e233 100644 --- a/core/java/android/app/DownloadManager.java +++ b/core/java/android/app/DownloadManager.java @@ -19,6 +19,7 @@ package android.app; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.annotation.SdkConstant.SdkConstantType; import android.content.ContentResolver; import android.content.ContentUris; @@ -51,18 +52,15 @@ import java.util.List; * request that a URI be downloaded to a particular destination file. The download manager will * conduct the download in the background, taking care of HTTP interactions and retrying downloads * after failures or across connectivity changes and system reboots. - * - * Instances of this class should be obtained through - * {@link android.content.Context#getSystemService(String)} by passing - * {@link android.content.Context#DOWNLOAD_SERVICE}. - * + * <p> * Apps that request downloads through this API should register a broadcast receiver for * {@link #ACTION_NOTIFICATION_CLICKED} to appropriately handle when the user clicks on a running * download in a notification or from the downloads UI. - * + * <p> * Note that the application must have the {@link android.Manifest.permission#INTERNET} * permission to use this class. */ +@SystemService(Context.DOWNLOAD_SERVICE) public class DownloadManager { /** diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index 4a0b644d758a..16b21f15353e 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -20,6 +20,7 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SystemService; import android.app.trust.ITrustManager; import android.content.Context; import android.content.Intent; @@ -46,12 +47,11 @@ import com.android.internal.widget.LockPatternUtils; import java.util.List; /** - * Class that can be used to lock and unlock the keyboard. Get an instance of this - * class by calling {@link android.content.Context#getSystemService(java.lang.String)} - * with argument {@link android.content.Context#KEYGUARD_SERVICE}. The + * Class that can be used to lock and unlock the keyboard. The * actual class to control the keyboard locking is * {@link android.app.KeyguardManager.KeyguardLock}. */ +@SystemService(Context.KEYGUARD_SERVICE) public class KeyguardManager { private static final String TAG = "KeyguardManager"; diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index df9b64cb7b86..03e9c0a1c9f7 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -22,6 +22,7 @@ import android.annotation.ColorInt; import android.annotation.DrawableRes; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; @@ -1009,6 +1010,7 @@ public class Notification implements Parcelable * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.NOTIFICATION_DURING_SETUP) public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup"; /** @@ -1110,6 +1112,7 @@ public class Notification implements Parcelable /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME) public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName"; /** diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 6c55548c2ea4..235b8d445b1c 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; +import android.annotation.SystemService; import android.annotation.TestApi; import android.app.Notification.Builder; import android.content.ComponentName; @@ -81,10 +82,6 @@ import java.util.Objects; * to the {@link #cancel(int)} or {@link #cancel(String, int)} method to clear * this notification. * - * <p> - * You do not instantiate this class directly; instead, retrieve it through - * {@link android.content.Context#getSystemService}. - * * <div class="special reference"> * <h3>Developer Guides</h3> * <p>For a guide to creating notifications, read the @@ -93,10 +90,9 @@ import java.util.Objects; * </div> * * @see android.app.Notification - * @see android.content.Context#getSystemService */ -public class NotificationManager -{ +@SystemService(Context.NOTIFICATION_SERVICE) +public class NotificationManager { private static String TAG = "NotificationManager"; private static boolean localLOGV = false; diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index c529e4ba1678..ea990ad2924e 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -16,6 +16,7 @@ package android.app; +import android.annotation.SystemService; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.ContentResolver; @@ -46,10 +47,6 @@ import java.util.List; * services are provided through methods in {@link android.app.Activity Activity} * and the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} * {@link android.content.Intent Intent}. - * If you do require direct access to the SearchManager, do not instantiate - * this class directly. Instead, retrieve it through - * {@link android.content.Context#getSystemService - * context.getSystemService(Context.SEARCH_SERVICE)}. * * <div class="special reference"> * <h3>Developer Guides</h3> @@ -58,9 +55,9 @@ import java.util.List; * <a href="{@docRoot}guide/topics/search/index.html">Search</a> developer guide.</p> * </div> */ +@SystemService(Context.SEARCH_SERVICE) public class SearchManager - implements DialogInterface.OnDismissListener, DialogInterface.OnCancelListener -{ + implements DialogInterface.OnDismissListener, DialogInterface.OnCancelListener { private static final boolean DBG = false; private static final String TAG = "SearchManager"; diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index ab301bdedcac..4a092140ed78 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -18,6 +18,7 @@ package android.app; import android.annotation.IntDef; +import android.annotation.SystemService; import android.content.Context; import android.os.Binder; import android.os.RemoteException; @@ -36,6 +37,7 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ +@SystemService(Context.STATUS_BAR_SERVICE) public class StatusBarManager { public static final int DISABLE_EXPAND = View.STATUS_BAR_DISABLE_EXPAND; diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index 07e257083fa2..bc616686eced 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -17,6 +17,7 @@ package android.app; import android.annotation.IntDef; +import android.annotation.SystemService; import android.annotation.TestApi; import android.content.Context; import android.content.res.Configuration; @@ -49,11 +50,8 @@ import java.lang.annotation.RetentionPolicy; * displayed allowing the user to exit dock mode. Thus the dock mode * represented here may be different than the current state of the underlying * dock event broadcast. - * - * <p>You do not instantiate this class directly; instead, retrieve it through - * {@link android.content.Context#getSystemService - * Context.getSystemService(Context.UI_MODE_SERVICE)}. */ +@SystemService(Context.UI_MODE_SERVICE) public class UiModeManager { private static final String TAG = "UiModeManager"; diff --git a/core/java/android/app/VrManager.java b/core/java/android/app/VrManager.java index 8014ecafa9a2..b40c96c6f0c8 100644 --- a/core/java/android/app/VrManager.java +++ b/core/java/android/app/VrManager.java @@ -1,19 +1,20 @@ package android.app; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.content.ComponentName; +import android.content.Context; import android.os.RemoteException; import android.service.vr.IVrManager; /** * Used to control aspects of a devices Virtual Reality (VR) capabilities. - * <p> - * You do not instantiate this class directly; instead, retrieve it through - * {@link android.content.Context#getSystemService}. * @hide */ @SystemApi +@SystemService(Context.VR_SERVICE) public class VrManager { private final IVrManager mService; @@ -29,11 +30,10 @@ public class VrManager { * remain in VR mode even if the foreground does not specify Vr mode being enabled. Mainly used * by VR viewers to indicate that a device is placed in a VR viewer. * - * <p>Requires {@link android.Manifest.permission#ACCESS_VR_MANAGER} permission.</p> - * * @see Activity#setVrModeEnabled(boolean, ComponentName) * @param enabled true if the device should be placed in persistent VR mode. */ + @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public void setPersistentVrModeEnabled(boolean enabled) { try { mService.setPersistentVrModeEnabled(enabled); @@ -46,13 +46,12 @@ public class VrManager { * Sets the resolution and DPI of the vr2d virtual display used to display 2D * applications in VR mode. * - * <p>Requires {@link android.Manifest.permission#ACCESS_VR_MANAGER} permission.</p> - * * @param vr2dDisplayProp properties to be set to the virtual display for * 2D applications in VR mode. * * {@hide} */ + @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public void setVr2dDisplayProperties( Vr2dDisplayProperties vr2dDisplayProp) { try { diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 8a33791adfdb..dfcbfc4f8e09 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -20,8 +20,10 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RawRes; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.annotation.SdkConstant.SdkConstantType; import android.content.ComponentName; import android.content.ContentResolver; @@ -80,13 +82,13 @@ import java.util.concurrent.TimeUnit; /** * Provides access to the system wallpaper. With WallpaperManager, you can * get the current wallpaper, get the desired dimensions for the wallpaper, set - * the wallpaper, and more. Get an instance of WallpaperManager with - * {@link #getInstance(android.content.Context) getInstance()}. + * the wallpaper, and more. * * <p> An app can check whether wallpapers are supported for the current user, by calling * {@link #isWallpaperSupported()}, and whether setting of wallpapers is allowed, by calling * {@link #isSetWallpaperAllowed()}. */ +@SystemService(Context.WALLPAPER_SERVICE) public class WallpaperManager { private static String TAG = "WallpaperManager"; private static boolean DEBUG = false; @@ -1485,6 +1487,7 @@ public class WallpaperManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_HINTS) public void setDisplayPadding(Rect padding) { try { if (sGlobals.mService == null) { @@ -1525,6 +1528,7 @@ public class WallpaperManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public void clearWallpaper() { clearWallpaper(FLAG_LOCK, mContext.getUserId()); clearWallpaper(FLAG_SYSTEM, mContext.getUserId()); @@ -1537,6 +1541,7 @@ public class WallpaperManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public void clearWallpaper(@SetWallpaperFlags int which, int userId) { if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); @@ -1552,12 +1557,10 @@ public class WallpaperManager { /** * Set the live wallpaper. * - * This can only be called by packages with android.permission.SET_WALLPAPER_COMPONENT - * permission. - * * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT) public boolean setWallpaperComponent(ComponentName name) { return setWallpaperComponent(name, UserHandle.myUserId()); } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 9e0d4e7b23c7..ff9425ebd155 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -20,9 +20,12 @@ import android.annotation.ColorInt; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UserIdInt; import android.annotation.WorkerThread; @@ -98,6 +101,7 @@ import java.util.Set; * "{@docRoot}guide/topics/admin/device-admin.html">Device Administration</a> developer * guide. </div> */ +@SystemService(Context.DEVICE_POLICY_SERVICE) public class DevicePolicyManager { private static String TAG = "DevicePolicyManager"; @@ -1621,6 +1625,7 @@ public class DevicePolicyManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean packageHasActiveAdmins(String packageName) { return packageHasActiveAdmins(packageName, myUserId()); } @@ -4532,11 +4537,10 @@ public class DevicePolicyManager { /** * @return device owner component name, even if it's running on a different user. * - * <p>Requires the MANAGE_USERS permission. - * * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public ComponentName getDeviceOwnerComponentOnAnyUser() { return getDeviceOwnerComponentInner(/* callingUserOnly =*/ false); } @@ -4620,6 +4624,7 @@ public class DevicePolicyManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public @Nullable String getDeviceOwner() { throwIfParentInstance("getDeviceOwner"); final ComponentName name = getDeviceOwnerComponentOnCallingUser(); @@ -4637,6 +4642,7 @@ public class DevicePolicyManager { */ @SystemApi @TestApi + @SuppressLint("Doclava125") public boolean isDeviceManaged() { try { return mService.hasDeviceOwner(); @@ -4649,11 +4655,10 @@ public class DevicePolicyManager { * Returns the device owner name. Note this method *will* return the device owner * name when it's running on a different user. * - * <p>Requires the MANAGE_USERS permission. - * * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwnerNameOnAnyUser() { throwIfParentInstance("getDeviceOwnerNameOnAnyUser"); if (mService != null) { @@ -4673,6 +4678,7 @@ public class DevicePolicyManager { */ @Deprecated @SystemApi + @SuppressLint("Doclava125") public @Nullable String getDeviceInitializerApp() { return null; } @@ -4684,6 +4690,7 @@ public class DevicePolicyManager { */ @Deprecated @SystemApi + @SuppressLint("Doclava125") public @Nullable ComponentName getDeviceInitializerComponent() { return null; } @@ -4706,6 +4713,7 @@ public class DevicePolicyManager { */ @Deprecated @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull ComponentName admin, @Deprecated String ownerName) throws IllegalArgumentException { throwIfParentInstance("setActiveProfileOwner"); @@ -5023,6 +5031,7 @@ public class DevicePolicyManager { * @throws IllegalArgumentException if the userId is invalid. */ @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public @Nullable String getProfileOwnerNameAsUser(int userId) throws IllegalArgumentException { throwIfParentInstance("getProfileOwnerNameAsUser"); if (mService != null) { @@ -6793,8 +6802,7 @@ public class DevicePolicyManager { * Called by the system update service to notify device and profile owners of pending system * updates. * - * The caller must hold {@link android.Manifest.permission#NOTIFY_PENDING_SYSTEM_UPDATE} - * permission. This method should only be used when it is unknown whether the pending system + * This method should only be used when it is unknown whether the pending system * update is a security patch. Otherwise, use * {@link #notifyPendingSystemUpdate(long, boolean)}. * @@ -6805,6 +6813,7 @@ public class DevicePolicyManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.NOTIFY_PENDING_SYSTEM_UPDATE) public void notifyPendingSystemUpdate(long updateReceivedTime) { throwIfParentInstance("notifyPendingSystemUpdate"); if (mService != null) { @@ -6820,8 +6829,7 @@ public class DevicePolicyManager { * Called by the system update service to notify device and profile owners of pending system * updates. * - * The caller must hold {@link android.Manifest.permission#NOTIFY_PENDING_SYSTEM_UPDATE} - * permission. This method should be used instead of {@link #notifyPendingSystemUpdate(long)} + * This method should be used instead of {@link #notifyPendingSystemUpdate(long)} * when it is known whether the pending system update is a security patch. * * @param updateReceivedTime The time as given by {@link System#currentTimeMillis()} @@ -6833,6 +6841,7 @@ public class DevicePolicyManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.NOTIFY_PENDING_SYSTEM_UPDATE) public void notifyPendingSystemUpdate(long updateReceivedTime, boolean isSecurityPatch) { throwIfParentInstance("notifyPendingSystemUpdate"); if (mService != null) { @@ -7541,6 +7550,7 @@ public class DevicePolicyManager { */ @SystemApi @TestApi + @SuppressLint("Doclava125") public @Nullable CharSequence getDeviceOwnerOrganizationName() { try { return mService.getDeviceOwnerOrganizationName(); @@ -7731,6 +7741,7 @@ public class DevicePolicyManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied() { try { mService.setDeviceProvisioningConfigApplied(); @@ -7751,6 +7762,7 @@ public class DevicePolicyManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioningConfigApplied() { try { return mService.isDeviceProvisioningConfigApplied(); diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java index 9d02f53b13bd..9f9b217069d8 100644 --- a/core/java/android/app/backup/BackupManager.java +++ b/core/java/android/app/backup/BackupManager.java @@ -16,6 +16,7 @@ package android.app.backup; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.ComponentName; import android.content.Context; @@ -324,6 +325,7 @@ public class BackupManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.BACKUP) public RestoreSession beginRestoreSession() { RestoreSession session = null; checkServiceBinder(); @@ -348,11 +350,10 @@ public class BackupManager { * mechanism was disabled will still be backed up properly if it is enabled * at some point in the future. * - * <p>Callers must hold the android.permission.BACKUP permission to use this method. - * * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.BACKUP) public void setBackupEnabled(boolean isEnabled) { checkServiceBinder(); if (sService != null) { @@ -367,11 +368,10 @@ public class BackupManager { /** * Report whether the backup mechanism is currently enabled. * - * <p>Callers must hold the android.permission.BACKUP permission to use this method. - * * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.BACKUP) public boolean isBackupEnabled() { checkServiceBinder(); if (sService != null) { @@ -390,11 +390,10 @@ public class BackupManager { * the archival restore dataset (if any). When disabled, no such attempt will * be made. * - * <p>Callers must hold the android.permission.BACKUP permission to use this method. - * * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.BACKUP) public void setAutoRestore(boolean isEnabled) { checkServiceBinder(); if (sService != null) { @@ -407,14 +406,14 @@ public class BackupManager { } /** - * Identify the currently selected transport. Callers must hold the - * android.permission.BACKUP permission to use this method. + * Identify the currently selected transport. * @return The name of the currently active backup transport. In case of * failure or if no transport is currently active, this method returns {@code null}. * * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.BACKUP) public String getCurrentTransport() { checkServiceBinder(); if (sService != null) { @@ -428,12 +427,12 @@ public class BackupManager { } /** - * Request a list of all available backup transports' names. Callers must - * hold the android.permission.BACKUP permission to use this method. + * Request a list of all available backup transports' names. * * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.BACKUP) public String[] listAllTransports() { checkServiceBinder(); if (sService != null) { @@ -449,8 +448,6 @@ public class BackupManager { /** * Specify the current backup transport. * - * <p> Callers must hold the android.permission.BACKUP permission to use this method. - * * @param transport The name of the transport to select. This should be one * of the names returned by {@link #listAllTransports()}. This is the String returned by * {@link BackupTransport#name()} for the particular transport. @@ -462,6 +459,7 @@ public class BackupManager { */ @Deprecated @SystemApi + @RequiresPermission(android.Manifest.permission.BACKUP) public String selectBackupTransport(String transport) { checkServiceBinder(); if (sService != null) { @@ -479,8 +477,6 @@ public class BackupManager { * This method is async because BackupManager might need to bind to the specified transport * which is in a separate process. * - * <p>Callers must hold the android.permission.BACKUP permission to use this method. - * * @param transport ComponentName of the service hosting the transport. This is different from * the transport's name that is returned by {@link BackupTransport#name()}. * @param listener A listener object to get a callback on the transport being selected. @@ -488,6 +484,7 @@ public class BackupManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.BACKUP) public void selectBackupTransport(ComponentName transport, SelectBackupTransportCallback listener) { checkServiceBinder(); @@ -510,11 +507,10 @@ public class BackupManager { * transport will still be asked to confirm via the usual requestBackupTime() * method. * - * <p>Callers must hold the android.permission.BACKUP permission to use this method. - * * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.BACKUP) public void backupNow() { checkServiceBinder(); if (sService != null) { @@ -530,8 +526,6 @@ public class BackupManager { * Ask the framework which dataset, if any, the given package's data would be * restored from if we were to install it right now. * - * <p>Callers must hold the android.permission.BACKUP permission to use this method. - * * @param packageName The name of the package whose most-suitable dataset we * wish to look up * @return The dataset token from which a restore should be attempted, or zero if @@ -540,6 +534,7 @@ public class BackupManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.BACKUP) public long getAvailableRestoreToken(String packageName) { checkServiceBinder(); if (sService != null) { @@ -555,14 +550,13 @@ public class BackupManager { /** * Ask the framework whether this app is eligible for backup. * - * <p>Callers must hold the android.permission.BACKUP permission to use this method. - * * @param packageName The name of the package. * @return Whether this app is eligible for backup. * * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.BACKUP) public boolean isAppEligibleForBackup(String packageName) { checkServiceBinder(); if (sService != null) { @@ -592,6 +586,7 @@ public class BackupManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.BACKUP) public int requestBackup(String[] packages, BackupObserver observer) { return requestBackup(packages, observer, null, 0); } @@ -615,6 +610,7 @@ public class BackupManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.BACKUP) public int requestBackup(String[] packages, BackupObserver observer, BackupManagerMonitor monitor, int flags) { checkServiceBinder(); @@ -638,11 +634,10 @@ public class BackupManager { * Cancel all running backups. After this call returns, no currently running backups will * interact with the selected transport. * - * <p>Callers must hold the android.permission.BACKUP permission to use this method. - * * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.BACKUP) public void cancelBackups() { checkServiceBinder(); if (sService != null) { diff --git a/core/java/android/app/job/JobScheduler.java b/core/java/android/app/job/JobScheduler.java index 1768828eb76c..3868439f092f 100644 --- a/core/java/android/app/job/JobScheduler.java +++ b/core/java/android/app/job/JobScheduler.java @@ -19,8 +19,11 @@ package android.app.job; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.content.ClipData; +import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.PersistableBundle; @@ -53,6 +56,7 @@ import java.util.List; * {@link android.content.Context#getSystemService * Context.getSystemService(Context.JOB_SCHEDULER_SERVICE)}. */ +@SystemService(Context.JOB_SCHEDULER_SERVICE) public abstract class JobScheduler { /** @hide */ @IntDef(prefix = { "RESULT_" }, value = { @@ -132,6 +136,7 @@ public abstract class JobScheduler { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public abstract @Result int scheduleAsPackage(@NonNull JobInfo job, @NonNull String packageName, int userId, String tag); diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java index 3c681f26f5c6..852cb8e09ddb 100644 --- a/core/java/android/app/trust/TrustManager.java +++ b/core/java/android/app/trust/TrustManager.java @@ -18,6 +18,8 @@ package android.app.trust; import android.Manifest; import android.annotation.RequiresPermission; +import android.annotation.SystemService; +import android.content.Context; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -29,6 +31,7 @@ import android.util.ArrayMap; * See {@link com.android.server.trust.TrustManagerService} * @hide */ +@SystemService(Context.TRUST_SERVICE) public class TrustManager { private static final int MSG_TRUST_CHANGED = 1; diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java index 6cd4e92383b2..ef262e046021 100644 --- a/core/java/android/app/usage/NetworkStatsManager.java +++ b/core/java/android/app/usage/NetworkStatsManager.java @@ -19,6 +19,7 @@ package android.app.usage; import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.Nullable; +import android.annotation.SystemService; import android.app.usage.NetworkStats.Bucket; import android.content.Context; import android.net.ConnectivityManager; @@ -82,6 +83,7 @@ import android.util.Log; * the above permission, even to access an app's own data usage, and carrier-privileged apps were * not included. */ +@SystemService(Context.NETWORK_STATS_SERVICE) public class NetworkStatsManager { private static final String TAG = "NetworkStatsManager"; private static final boolean DBG = false; diff --git a/core/java/android/app/usage/StorageStatsManager.java b/core/java/android/app/usage/StorageStatsManager.java index 0b2b1900c4e0..7c680794d140 100644 --- a/core/java/android/app/usage/StorageStatsManager.java +++ b/core/java/android/app/usage/StorageStatsManager.java @@ -20,6 +20,7 @@ import static android.os.storage.StorageManager.convert; import android.annotation.BytesLong; import android.annotation.NonNull; +import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.WorkerThread; import android.content.Context; @@ -50,6 +51,7 @@ import java.util.UUID; * application. * </p> */ +@SystemService(Context.STORAGE_STATS_SERVICE) public class StorageStatsManager { private final Context mContext; private final IStorageStatsManager mService; diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index 75a4a535186e..1f939f996c68 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -16,7 +16,9 @@ package android.app.usage; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.content.Context; import android.content.pm.ParceledListSlice; import android.os.RemoteException; @@ -51,6 +53,7 @@ import java.util.Map; * the permission implies intention to use the API and the user of the device can grant permission * through the Settings application. */ +@SystemService(Context.USAGE_STATS_SERVICE) public final class UsageStatsManager { /** @@ -252,7 +255,6 @@ public final class UsageStatsManager { * Temporarily whitelist the specified app for a short duration. This is to allow an app * receiving a high priority message to be able to access the network and acquire wakelocks * even if the device is in power-save mode or the app is currently considered inactive. - * The caller must hold the CHANGE_DEVICE_IDLE_TEMP_WHITELIST permission. * @param packageName The package name of the app to whitelist. * @param duration Duration to whitelist the app for, in milliseconds. It is recommended that * this be limited to 10s of seconds. Requested duration will be clamped to a few minutes. @@ -261,6 +263,7 @@ public final class UsageStatsManager { * @see #isAppInactive(String) */ @SystemApi + @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(String packageName, long duration, UserHandle user) { try { mService.whitelistAppTemporarily(packageName, duration, user.getIdentifier()); diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index 6327f34ebd5e..969b19ee48ac 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -20,6 +20,7 @@ import android.annotation.BroadcastBehavior; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; +import android.annotation.SystemService; import android.annotation.SdkConstant.SdkConstantType; import android.app.PendingIntent; import android.content.ComponentName; @@ -51,6 +52,7 @@ import java.util.List; * <a href="{@docRoot}guide/topics/appwidgets/index.html">App Widgets</a> developer guide.</p> * </div> */ +@SystemService(Context.APPWIDGET_SERVICE) public class AppWidgetManager { /** diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java index c7191ba2638b..e2fa38a9309f 100644 --- a/core/java/android/bluetooth/BluetoothManager.java +++ b/core/java/android/bluetooth/BluetoothManager.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresPermission; +import android.annotation.SystemService; import android.content.Context; import android.os.RemoteException; import android.util.Log; @@ -48,6 +49,7 @@ import java.util.List; * @see Context#getSystemService * @see BluetoothAdapter#getDefaultAdapter() */ +@SystemService(Context.BLUETOOTH_SERVICE) public final class BluetoothManager { private static final String TAG = "BluetoothManager"; private static final boolean DBG = true; diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index 4e70e3fe8ce5..dabe608c038f 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -21,6 +21,7 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemService; import android.app.Activity; import android.app.Application; import android.app.PendingIntent; @@ -47,6 +48,7 @@ import java.util.List; * * @see AssociationRequest */ +@SystemService(Context.COMPANION_DEVICE_SERVICE) public final class CompanionDeviceManager { private static final boolean DEBUG = false; diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java index f1c2f342eb3e..718e465bf0de 100644 --- a/core/java/android/content/ClipboardManager.java +++ b/core/java/android/content/ClipboardManager.java @@ -16,6 +16,7 @@ package android.content; +import android.annotation.SystemService; import android.os.Handler; import android.os.Message; import android.os.RemoteException; @@ -29,10 +30,6 @@ import java.util.ArrayList; * the global clipboard. * * <p> - * You do not instantiate this class directly; instead, retrieve it through - * {@link android.content.Context#getSystemService}. - * - * <p> * The ClipboardManager API itself is very simple: it consists of methods * to atomically get and set the current primary clipboard data. That data * is expressed as a {@link ClipData} object, which defines the protocol @@ -44,9 +41,8 @@ import java.util.ArrayList; * <a href="{@docRoot}guide/topics/clipboard/copy-paste.html">Copy and Paste</a> * developer guide.</p> * </div> - * - * @see android.content.Context#getSystemService */ +@SystemService(Context.CLIPBOARD_SERVICE) public class ClipboardManager extends android.text.ClipboardManager { private final Context mContext; private final IClipboard mService; diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 9ad33ee0379c..2f795385b2e5 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1632,13 +1632,13 @@ public abstract class Context { /** * Version of {@link #startActivity(Intent)} that allows you to specify the * user the activity will be started for. This is not available to applications - * that are not pre-installed on the system image. Using it requires holding - * the INTERACT_ACROSS_USERS_FULL permission. + * that are not pre-installed on the system image. * @param intent The description of the activity to start. * @param user The UserHandle of the user to start this activity for. * @throws ActivityNotFoundException * @hide */ + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void startActivityAsUser(@RequiresPermission Intent intent, UserHandle user) { throw new RuntimeException("Not implemented. Must override in a subclass."); } @@ -1674,8 +1674,7 @@ public abstract class Context { /** * Version of {@link #startActivity(Intent, Bundle)} that allows you to specify the * user the activity will be started for. This is not available to applications - * that are not pre-installed on the system image. Using it requires holding - * the INTERACT_ACROSS_USERS_FULL permission. + * that are not pre-installed on the system image. * @param intent The description of the activity to start. * @param options Additional options for how the Activity should be started. * May be null if there are no options. See {@link android.app.ActivityOptions} @@ -1685,6 +1684,7 @@ public abstract class Context { * @throws ActivityNotFoundException * @hide */ + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void startActivityAsUser(@RequiresPermission Intent intent, @Nullable Bundle options, UserHandle userId) { throw new RuntimeException("Not implemented. Must override in a subclass."); @@ -1783,6 +1783,7 @@ public abstract class Context { * @see #startActivities(Intent[]) * @see PackageManager#resolveActivity */ + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) { throw new RuntimeException("Not implemented. Must override in a subclass."); } @@ -2083,20 +2084,19 @@ public abstract class Context { /** * Version of {@link #sendBroadcast(Intent)} that allows you to specify the * user the broadcast will be sent to. This is not available to applications - * that are not pre-installed on the system image. Using it requires holding - * the INTERACT_ACROSS_USERS permission. + * that are not pre-installed on the system image. * @param intent The intent to broadcast * @param user UserHandle to send the intent to. * @see #sendBroadcast(Intent) */ + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission Intent intent, UserHandle user); /** * Version of {@link #sendBroadcast(Intent, String)} that allows you to specify the * user the broadcast will be sent to. This is not available to applications - * that are not pre-installed on the system image. Using it requires holding - * the INTERACT_ACROSS_USERS permission. + * that are not pre-installed on the system image. * * @param intent The Intent to broadcast; all receivers matching this * Intent will receive the broadcast. @@ -2107,14 +2107,14 @@ public abstract class Context { * * @see #sendBroadcast(Intent, String) */ + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission Intent intent, UserHandle user, @Nullable String receiverPermission); /** * Version of {@link #sendBroadcast(Intent, String, Bundle)} that allows you to specify the * user the broadcast will be sent to. This is not available to applications - * that are not pre-installed on the system image. Using it requires holding - * the INTERACT_ACROSS_USERS permission. + * that are not pre-installed on the system image. * * @param intent The Intent to broadcast; all receivers matching this * Intent will receive the broadcast. @@ -2129,14 +2129,14 @@ public abstract class Context { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission Intent intent, UserHandle user, @Nullable String receiverPermission, @Nullable Bundle options); /** * Version of {@link #sendBroadcast(Intent, String)} that allows you to specify the * user the broadcast will be sent to. This is not available to applications - * that are not pre-installed on the system image. Using it requires holding - * the INTERACT_ACROSS_USERS permission. + * that are not pre-installed on the system image. * * @param intent The Intent to broadcast; all receivers matching this * Intent will receive the broadcast. @@ -2150,6 +2150,7 @@ public abstract class Context { * * @hide */ + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission Intent intent, UserHandle user, @Nullable String receiverPermission, int appOp); @@ -2158,8 +2159,7 @@ public abstract class Context { * {@link #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)} * that allows you to specify the * user the broadcast will be sent to. This is not available to applications - * that are not pre-installed on the system image. Using it requires holding - * the INTERACT_ACROSS_USERS permission. + * that are not pre-installed on the system image. * * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts. * @@ -2183,6 +2183,7 @@ public abstract class Context { * * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) */ + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendOrderedBroadcastAsUser(@RequiresPermission Intent intent, UserHandle user, @Nullable String receiverPermission, BroadcastReceiver resultReceiver, @Nullable Handler scheduler, int initialCode, @Nullable String initialData, @@ -2194,6 +2195,7 @@ public abstract class Context { * BroadcastReceiver, Handler, int, String, Bundle) * @hide */ + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, @Nullable String receiverPermission, int appOp, BroadcastReceiver resultReceiver, @Nullable Handler scheduler, int initialCode, @Nullable String initialData, @@ -2205,6 +2207,7 @@ public abstract class Context { * BroadcastReceiver, Handler, int, String, Bundle) * @hide */ + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, @Nullable String receiverPermission, int appOp, @Nullable Bundle options, BroadcastReceiver resultReceiver, @Nullable Handler scheduler, int initialCode, @@ -2309,8 +2312,7 @@ public abstract class Context { /** * <p>Version of {@link #sendStickyBroadcast(Intent)} that allows you to specify the * user the broadcast will be sent to. This is not available to applications - * that are not pre-installed on the system image. Using it requires holding - * the INTERACT_ACROSS_USERS permission. + * that are not pre-installed on the system image. * * @deprecated Sticky broadcasts should not be used. They provide no security (anyone * can access them), no protection (anyone can modify them), and many other problems. @@ -2326,6 +2328,10 @@ public abstract class Context { * @see #sendBroadcast(Intent) */ @Deprecated + @RequiresPermission(allOf = { + android.Manifest.permission.INTERACT_ACROSS_USERS, + android.Manifest.permission.BROADCAST_STICKY + }) public abstract void sendStickyBroadcastAsUser(@RequiresPermission Intent intent, UserHandle user); @@ -2334,6 +2340,10 @@ public abstract class Context { * This is just here for sending CONNECTIVITY_ACTION. */ @Deprecated + @RequiresPermission(allOf = { + android.Manifest.permission.INTERACT_ACROSS_USERS, + android.Manifest.permission.BROADCAST_STICKY + }) public abstract void sendStickyBroadcastAsUser(@RequiresPermission Intent intent, UserHandle user, Bundle options); @@ -2342,8 +2352,7 @@ public abstract class Context { * {@link #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)} * that allows you to specify the * user the broadcast will be sent to. This is not available to applications - * that are not pre-installed on the system image. Using it requires holding - * the INTERACT_ACROSS_USERS permission. + * that are not pre-installed on the system image. * * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts. * @@ -2371,6 +2380,10 @@ public abstract class Context { * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle) */ @Deprecated + @RequiresPermission(allOf = { + android.Manifest.permission.INTERACT_ACROSS_USERS, + android.Manifest.permission.BROADCAST_STICKY + }) public abstract void sendStickyOrderedBroadcastAsUser(@RequiresPermission Intent intent, UserHandle user, BroadcastReceiver resultReceiver, @Nullable Handler scheduler, int initialCode, @Nullable String initialData, @@ -2379,8 +2392,7 @@ public abstract class Context { /** * <p>Version of {@link #removeStickyBroadcast(Intent)} that allows you to specify the * user the broadcast will be sent to. This is not available to applications - * that are not pre-installed on the system image. Using it requires holding - * the INTERACT_ACROSS_USERS permission. + * that are not pre-installed on the system image. * * <p>You must hold the {@link android.Manifest.permission#BROADCAST_STICKY} * permission in order to use this API. If you do not hold that @@ -2398,6 +2410,10 @@ public abstract class Context { * @see #sendStickyBroadcastAsUser */ @Deprecated + @RequiresPermission(allOf = { + android.Manifest.permission.INTERACT_ACROSS_USERS, + android.Manifest.permission.BROADCAST_STICKY + }) public abstract void removeStickyBroadcastAsUser(@RequiresPermission Intent intent, UserHandle user); @@ -2564,9 +2580,7 @@ public abstract class Context { * @hide * Same as {@link #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) * but for a specific user. This receiver will receiver broadcasts that - * are sent to the requested user. It - * requires holding the {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} - * permission. + * are sent to the requested user. * * @param receiver The BroadcastReceiver to handle the broadcast. * @param user UserHandle to send the intent to. @@ -2585,6 +2599,7 @@ public abstract class Context { * @see #unregisterReceiver */ @Nullable + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user, IntentFilter filter, @Nullable String broadcastPermission, @Nullable Handler scheduler); @@ -2693,6 +2708,7 @@ public abstract class Context { * @hide like {@link #startForegroundService(Intent)} but for a specific user. */ @Nullable + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract ComponentName startForegroundServiceAsUser(Intent service, UserHandle user); /** @@ -2730,11 +2746,13 @@ public abstract class Context { * @hide like {@link #startService(Intent)} but for a specific user. */ @Nullable + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract ComponentName startServiceAsUser(Intent service, UserHandle user); /** * @hide like {@link #stopService(Intent)} but for a specific user. */ + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract boolean stopServiceAsUser(Intent service, UserHandle user); /** @@ -2794,6 +2812,7 @@ public abstract class Context { */ @SystemApi @SuppressWarnings("unused") + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean bindServiceAsUser(@RequiresPermission Intent service, ServiceConnection conn, int flags, UserHandle user) { throw new RuntimeException("Not implemented. Must override in a subclass."); @@ -2805,6 +2824,7 @@ public abstract class Context { * * @hide */ + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags, Handler handler, UserHandle user) { throw new RuntimeException("Not implemented. Must override in a subclass."); diff --git a/core/java/android/content/RestrictionsManager.java b/core/java/android/content/RestrictionsManager.java index 88aae6655428..b463ec6277e7 100644 --- a/core/java/android/content/RestrictionsManager.java +++ b/core/java/android/content/RestrictionsManager.java @@ -16,6 +16,7 @@ package android.content; +import android.annotation.SystemService; import android.app.Activity; import android.app.admin.DevicePolicyManager; import android.content.pm.ApplicationInfo; @@ -120,6 +121,7 @@ import java.util.List; * @see DevicePolicyManager#setRestrictionsProvider(ComponentName, ComponentName) * @see DevicePolicyManager#setApplicationRestrictions(ComponentName, String, Bundle) */ +@SystemService(Context.RESTRICTIONS_SERVICE) public class RestrictionsManager { private static final String TAG = "RestrictionsManager"; diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 8ead0ec612f8..ed41e79e2c6c 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; +import android.annotation.SystemService; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.TestApi; import android.app.PendingIntent; @@ -79,6 +80,7 @@ import java.util.List; * Note as of Android O, apps on a managed profile are no longer allowed to access apps on the * main profile. Apps can only access profiles returned by {@link #getProfiles()}. */ +@SystemService(Context.LAUNCHER_APPS_SERVICE) public class LauncherApps { static final String TAG = "LauncherApps"; diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 4e112331a1ed..7f3f35ffd4f3 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -473,6 +473,7 @@ public class PackageInstaller { /** {@hide} */ @SystemApi + @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int sessionId, boolean accepted) { try { mInstaller.setPermissionsResult(sessionId, accepted); @@ -1156,6 +1157,7 @@ public class PackageInstaller { /** {@hide} */ @SystemApi + @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE) public void setAllocateAggressive(boolean allocateAggressive) { if (allocateAggressive) { installFlags |= PackageManager.INSTALL_ALLOCATE_AGGRESSIVE; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index c98c59fe9408..6bc7d42a14b2 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3243,8 +3243,7 @@ public abstract class PackageManager { /** * Return a List of all packages that are installed on the device, for a - * specific user. Requesting a list of installed packages for another user - * will require the permission INTERACT_ACROSS_USERS_FULL. + * specific user. * * @param flags Additional option flags to modify the data returned. * @param userId The user for whom the installed packages are to be listed @@ -3259,6 +3258,7 @@ public abstract class PackageManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract List<PackageInfo> getInstalledPackagesAsUser(@PackageInfoFlags int flags, @UserIdInt int userId); @@ -3400,6 +3400,7 @@ public abstract class PackageManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String packageName, @NonNull String permissionName, @NonNull UserHandle user); @@ -3425,6 +3426,7 @@ public abstract class PackageManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public abstract void revokeRuntimePermission(@NonNull String packageName, @NonNull String permissionName, @NonNull UserHandle user); @@ -3439,6 +3441,10 @@ public abstract class PackageManager { * @hide */ @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, + android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS + }) public abstract @PermissionFlags int getPermissionFlags(String permissionName, String packageName, @NonNull UserHandle user); @@ -3455,6 +3461,10 @@ public abstract class PackageManager { * @hide */ @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, + android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS + }) public abstract void updatePermissionFlags(String permissionName, String packageName, @PermissionFlags int flagMask, @PermissionFlags int flagValues, @NonNull UserHandle user); @@ -4754,6 +4764,7 @@ public abstract class PackageManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT) public abstract void verifyIntentFilter(int verificationId, int verificationCode, List<String> failedDomains); @@ -4801,6 +4812,7 @@ public abstract class PackageManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS) public abstract boolean updateIntentVerificationStatusAsUser(String packageName, int status, @UserIdInt int userId); @@ -4861,6 +4873,7 @@ public abstract class PackageManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS) public abstract boolean setDefaultBrowserPackageNameAsUser(String packageName, @UserIdInt int userId); @@ -5324,6 +5337,7 @@ public abstract class PackageManager { * @hide */ @SystemApi + @RequiresPermission(Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS) public abstract void removeOnPermissionsChangeListener(OnPermissionsChangedListener listener); /** diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java index f779aeb879e2..c0b82b4dfee3 100644 --- a/core/java/android/content/pm/ShortcutManager.java +++ b/core/java/android/content/pm/ShortcutManager.java @@ -17,6 +17,7 @@ package android.content.pm; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UserIdInt; import android.app.Activity; @@ -24,6 +25,7 @@ import android.app.usage.UsageStatsManager; import android.content.Context; import android.content.Intent; import android.content.IntentSender; +import android.graphics.drawable.AdaptiveIconDrawable; import android.os.Build.VERSION_CODES; import android.os.RemoteException; import android.os.ServiceManager; @@ -335,6 +337,14 @@ import java.util.List; * {@link #isRequestPinShortcutSupported()}. Based on this return value, you might decide to hide * the option in your app that allows users to pin a shortcut. * + * <p class="note"><strong>Note:</strong> See also the support library APIs + * {@link android.support.v4.content.pm.ShortcutManagerCompat#isRequestPinShortcutSupported( + * Context)} and + * {@link android.support.v4.content.pm.ShortcutManagerCompat#requestPinShortcut( + * Context, ShortcutInfoCompat, IntentSender)}, which works on Android versions lower than + * {@link VERSION_CODES#O} by falling back to the deprecated private intent + * {@code com.android.launcher.action.INSTALL_SHORTCUT}. + * * <h4>Custom Activity for Pinning Shortcuts</h4> * * <p>You can also create a specialized activity that helps users create shortcuts, complete with @@ -569,6 +579,7 @@ import java.util.List; * All shortcut information is stored in credential encrypted storage, so no shortcuts can be * accessed when the user is locked. */ +@SystemService(Context.SHORTCUT_SERVICE) public class ShortcutManager { private static final String TAG = "ShortcutManager"; @@ -889,7 +900,7 @@ public class ShortcutManager { * * <p> Note that this method returns max width of icon's visible part. Hence, it does not take * into account the inset introduced by {@link AdaptiveIconDrawable}. To calculate bitmap image - * to function as {@link AcaptiveIconDrawable}, multiply + * to function as {@link AdaptiveIconDrawable}, multiply * 1 + 2 * {@link AdaptiveIconDrawable#getExtraInsetFraction()} to the returned size. */ public int getIconMaxWidth() { @@ -938,8 +949,15 @@ public class ShortcutManager { * Return {@code TRUE} if the app is running on a device whose default launcher supports * {@link #requestPinShortcut(ShortcutInfo, IntentSender)}. * - * <p><b>Note:</b> The return value may change in subsequent calls, if the user changes - * the default launcher app. + * <p>The return value may change in subsequent calls if the user changes the default launcher + * app. + * + * <p><b>Note:</b> See also the support library counterpart + * {@link android.support.v4.content.pm.ShortcutManagerCompat#isRequestPinShortcutSupported( + * Context)}, which supports Android versions lower than {@link VERSION_CODES#O} using the + * legacy private intent {@code com.android.launcher.action.INSTALL_SHORTCUT}. + * + * @see #requestPinShortcut(ShortcutInfo, IntentSender) */ public boolean isRequestPinShortcutSupported() { try { @@ -963,6 +981,12 @@ public class ShortcutManager { * package calls this API multiple times in a row. One possible strategy is to ignore any * previous requests. * + * <p><b>Note:</b> See also the support library counterpart + * {@link android.support.v4.content.pm.ShortcutManagerCompat#requestPinShortcut( + * Context, ShortcutInfoCompat, IntentSender)}, + * which supports Android versions lower than {@link VERSION_CODES#O} using the + * legacy private intent {@code com.android.launcher.action.INSTALL_SHORTCUT}. + * * @param shortcut Shortcut to pin. If an app wants to pin an existing (either static * or dynamic) shortcut, then it only needs to have an ID. Although other fields don't have * to be set, the target shortcut must be enabled. diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 88c1627f955b..417a95fbc417 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -16,10 +16,7 @@ package android.content.res; -import android.graphics.Point; import android.graphics.Rect; -import android.util.DisplayMetrics; -import android.view.Display; import android.view.DisplayInfo; import com.android.internal.util.XmlUtils; @@ -45,6 +42,12 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Locale; +import static android.view.Surface.ROTATION_UNDEFINED; +import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_90; +import static android.view.Surface.ROTATION_180; +import static android.view.Surface.ROTATION_270; + /** * This class describes all device configuration information that can * impact the resources the application retrieves. This includes both @@ -600,6 +603,13 @@ public final class Configuration implements Parcelable, Comparable<Configuration */ public int orientation; + /** + * The rotation used at the time orientation was determined. + * TODO(b/36812336): Move rotation out of {@link Configuration}. + * {@hide} + */ + private int rotation; + /** Constant for {@link #uiMode}: bits that encode the mode type. */ public static final int UI_MODE_TYPE_MASK = 0x0f; /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK} @@ -887,6 +897,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration navigation = o.navigation; navigationHidden = o.navigationHidden; orientation = o.orientation; + rotation = o.rotation; screenLayout = o.screenLayout; colorMode = o.colorMode; uiMode = o.uiMode; @@ -990,6 +1001,14 @@ public final class Configuration implements Parcelable, Comparable<Configuration case ORIENTATION_PORTRAIT: sb.append(" port"); break; default: sb.append(" orien="); sb.append(orientation); break; } + switch (rotation) { + case ROTATION_UNDEFINED: sb.append(" ?rotation"); break; + case ROTATION_0: sb.append(" rot0"); break; + case ROTATION_90: sb.append(" rot90"); break; + case ROTATION_180: sb.append(" rot180"); break; + case ROTATION_270: sb.append(" rot270"); break; + default: sb.append(" rot="); sb.append(rotation); break; + } switch ((uiMode&UI_MODE_TYPE_MASK)) { case UI_MODE_TYPE_UNDEFINED: sb.append(" ?uimode"); break; case UI_MODE_TYPE_NORMAL: /* normal is not interesting to print */ break; @@ -1077,6 +1096,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration navigation = NAVIGATION_UNDEFINED; navigationHidden = NAVIGATIONHIDDEN_UNDEFINED; orientation = ORIENTATION_UNDEFINED; + rotation = ROTATION_UNDEFINED; screenLayout = SCREENLAYOUT_UNDEFINED; colorMode = COLOR_MODE_UNDEFINED; uiMode = UI_MODE_TYPE_UNDEFINED; @@ -1185,6 +1205,11 @@ public final class Configuration implements Parcelable, Comparable<Configuration changed |= ActivityInfo.CONFIG_ORIENTATION; orientation = delta.orientation; } + if (delta.rotation != ROTATION_UNDEFINED + && rotation != delta.rotation) { + changed |= ActivityInfo.CONFIG_ORIENTATION; + rotation = delta.rotation; + } if (((delta.screenLayout & SCREENLAYOUT_SIZE_MASK) != SCREENLAYOUT_SIZE_UNDEFINED) && (delta.screenLayout & SCREENLAYOUT_SIZE_MASK) @@ -1379,6 +1404,10 @@ public final class Configuration implements Parcelable, Comparable<Configuration && orientation != delta.orientation) { changed |= ActivityInfo.CONFIG_ORIENTATION; } + if ((compareUndefined || delta.rotation != ROTATION_UNDEFINED) + && rotation != delta.rotation) { + changed |= ActivityInfo.CONFIG_ORIENTATION; + } if ((compareUndefined || getScreenLayoutNoDirection(delta.screenLayout) != (SCREENLAYOUT_SIZE_UNDEFINED | SCREENLAYOUT_LONG_UNDEFINED)) && getScreenLayoutNoDirection(screenLayout) != @@ -1515,6 +1544,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration dest.writeInt(navigation); dest.writeInt(navigationHidden); dest.writeInt(orientation); + dest.writeInt(rotation); dest.writeInt(screenLayout); dest.writeInt(colorMode); dest.writeInt(uiMode); @@ -1551,6 +1581,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration navigation = source.readInt(); navigationHidden = source.readInt(); orientation = source.readInt(); + rotation = source.readInt(); screenLayout = source.readInt(); colorMode = source.readInt(); uiMode = source.readInt(); @@ -1635,6 +1666,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration if (n != 0) return n; n = this.orientation - that.orientation; if (n != 0) return n; + n = this.rotation - that.rotation; + if (n != 0) return n; n = this.colorMode - that.colorMode; if (n != 0) return n; n = this.screenLayout - that.screenLayout; @@ -1766,6 +1799,24 @@ public final class Configuration implements Parcelable, Comparable<Configuration /** * @hide * + * Setter for orientation converts from {@link Surface} values to internal representation. + */ + public void setRotation(int rotation) { + this.rotation = rotation; + } + + /** + * @hide + * + * Getter for orientation. Converts from internal representation to {@link Surface} values. + */ + public int getRotation() { + return rotation != ROTATION_UNDEFINED ? rotation : ROTATION_0; + } + + /** + * @hide + * * Clears the locale without changing layout direction. */ public void clearLocales() { @@ -2000,6 +2051,23 @@ public final class Configuration implements Parcelable, Comparable<Configuration break; } + switch (config.rotation) { + case ROTATION_0: + parts.add("rot0"); + break; + case ROTATION_90: + parts.add("rot90"); + break; + case ROTATION_180: + parts.add("rot180"); + break; + case ROTATION_270: + parts.add("rot270"); + break; + default: + break; + } + switch (config.uiMode & Configuration.UI_MODE_TYPE_MASK) { case Configuration.UI_MODE_TYPE_APPLIANCE: parts.add("appliance"); @@ -2194,6 +2262,10 @@ public final class Configuration implements Parcelable, Comparable<Configuration delta.orientation = change.orientation; } + if (base.rotation != change.rotation) { + base.rotation = change.rotation; + } + if ((base.screenLayout & SCREENLAYOUT_SIZE_MASK) != (change.screenLayout & SCREENLAYOUT_SIZE_MASK)) { delta.screenLayout |= change.screenLayout & SCREENLAYOUT_SIZE_MASK; @@ -2265,6 +2337,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration private static final String XML_ATTR_NAVIGATION = "nav"; private static final String XML_ATTR_NAVIGATION_HIDDEN = "navHid"; private static final String XML_ATTR_ORIENTATION = "ori"; + private static final String XML_ATTR_ROTATION = "rot"; private static final String XML_ATTR_SCREEN_LAYOUT = "scrLay"; private static final String XML_ATTR_COLOR_MODE = "clrMod"; private static final String XML_ATTR_UI_MODE = "ui"; @@ -2324,6 +2397,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration DENSITY_DPI_UNDEFINED); configOut.appBounds = Rect.unflattenFromString(XmlUtils.readStringAttribute(parser, XML_ATTR_APP_BOUNDS)); + configOut.rotation = XmlUtils.readIntAttribute(parser, XML_ATTR_ROTATION, + ROTATION_UNDEFINED); // For persistence, we don't care about assetsSeq, so do not read it out. } @@ -2400,6 +2475,10 @@ public final class Configuration implements Parcelable, Comparable<Configuration config.appBounds.flattenToString()); } + if (config.rotation != ROTATION_UNDEFINED) { + XmlUtils.writeIntAttribute(xml, XML_ATTR_ROTATION, config.rotation); + } + // For persistence, we do not care about assetsSeq, so do not write it out. } } diff --git a/core/java/android/hardware/ConsumerIrManager.java b/core/java/android/hardware/ConsumerIrManager.java index b221e1604949..c7a33ffa1b0f 100644 --- a/core/java/android/hardware/ConsumerIrManager.java +++ b/core/java/android/hardware/ConsumerIrManager.java @@ -16,6 +16,7 @@ package android.hardware; +import android.annotation.SystemService; import android.content.Context; import android.os.RemoteException; import android.os.ServiceManager; @@ -24,14 +25,8 @@ import android.util.Log; /** * Class that operates consumer infrared on the device. - * - * <p> - * To obtain an instance of the system infrared transmitter, call - * {@link android.content.Context#getSystemService(java.lang.String) - * Context.getSystemService()} with - * {@link android.content.Context#CONSUMER_IR_SERVICE} as the argument. - * </p> */ +@SystemService(Context.CONSUMER_IR_SERVICE) public final class ConsumerIrManager { private static final String TAG = "ConsumerIr"; diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index ed563914a46e..4bc62b1d04d3 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -17,6 +17,8 @@ package android.hardware; import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.content.Context; import android.os.Build; import android.os.Handler; import android.os.MemoryFile; @@ -30,10 +32,7 @@ import java.util.List; /** * <p> * SensorManager lets you access the device's {@link android.hardware.Sensor - * sensors}. Get an instance of this class by calling - * {@link android.content.Context#getSystemService(java.lang.String) - * Context.getSystemService()} with the argument - * {@link android.content.Context#SENSOR_SERVICE}. + * sensors}. * </p> * <p> * Always make sure to disable sensors you don't need, especially when your @@ -79,6 +78,7 @@ import java.util.List; * @see Sensor * */ +@SystemService(Context.SENSOR_SERVICE) public abstract class SensorManager { /** @hide */ protected static final String TAG = "SensorManager"; diff --git a/core/java/android/hardware/SerialManager.java b/core/java/android/hardware/SerialManager.java index 83f7649ff326..610f6a587e51 100644 --- a/core/java/android/hardware/SerialManager.java +++ b/core/java/android/hardware/SerialManager.java @@ -16,6 +16,7 @@ package android.hardware; +import android.annotation.SystemService; import android.content.Context; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -25,6 +26,7 @@ import java.io.IOException; /** * @hide */ +@SystemService(Context.SERIAL_SERVICE) public class SerialManager { private static final String TAG = "SerialManager"; diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index f61032ef8250..1b150bfca63a 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -17,6 +17,7 @@ package android.hardware.camera2; import android.annotation.RequiresPermission; +import android.annotation.SystemService; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -44,15 +45,11 @@ import java.util.ArrayList; * <p>A system service manager for detecting, characterizing, and connecting to * {@link CameraDevice CameraDevices}.</p> * - * <p>You can get an instance of this class by calling - * {@link android.content.Context#getSystemService(String) Context.getSystemService()}.</p> - * - * <pre>CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);</pre> - * * <p>For more details about communicating with camera devices, read the Camera * developer guide or the {@link android.hardware.camera2 camera2} * package documentation.</p> */ +@SystemService(Context.CAMERA_SERVICE) public final class CameraManager { private static final String TAG = "CameraManager"; diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 266be9a1dd0f..6a02b6b2e6e7 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -18,6 +18,7 @@ package android.hardware.display; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemService; import android.content.Context; import android.media.projection.MediaProjection; import android.os.Handler; @@ -29,13 +30,8 @@ import java.util.ArrayList; /** * Manages the properties of attached displays. - * <p> - * Get an instance of this class by calling - * {@link android.content.Context#getSystemService(java.lang.String) - * Context.getSystemService()} with the argument - * {@link android.content.Context#DISPLAY_SERVICE}. - * </p> */ +@SystemService(Context.DISPLAY_SERVICE) public final class DisplayManager { private static final String TAG = "DisplayManager"; private static final boolean DEBUG = false; diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 324a08ca9e1b..b51a7919e3bf 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -19,6 +19,7 @@ package android.hardware.fingerprint; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SystemService; import android.app.ActivityManager; import android.content.Context; import android.os.Binder; @@ -47,12 +48,8 @@ import static android.Manifest.permission.USE_FINGERPRINT; /** * A class that coordinates access to the fingerprint hardware. - * <p> - * Use {@link android.content.Context#getSystemService(java.lang.String)} - * with argument {@link android.content.Context#FINGERPRINT_SERVICE} to get - * an instance of this class. */ - +@SystemService(Context.FINGERPRINT_SERVICE) public class FingerprintManager { private static final String TAG = "FingerprintManager"; private static final boolean DEBUG = true; diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java index 27e2a5072287..a772cbe43196 100644 --- a/core/java/android/hardware/hdmi/HdmiControlManager.java +++ b/core/java/android/hardware/hdmi/HdmiControlManager.java @@ -17,9 +17,13 @@ package android.hardware.hdmi; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SuppressLint; +import android.content.Context; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.os.RemoteException; import android.util.ArrayMap; import android.util.Log; @@ -37,6 +41,7 @@ import android.util.Log; * @hide */ @SystemApi +@SystemService(Context.HDMI_CONTROL_SERVICE) public final class HdmiControlManager { private static final String TAG = "HdmiControlManager"; @@ -295,6 +300,7 @@ public final class HdmiControlManager { * See {@link HdmiDeviceInfo#DEVICE_TV} */ @Nullable + @SuppressLint("Doclava125") public HdmiClient getClient(int type) { if (mService == null) { return null; @@ -319,6 +325,7 @@ public final class HdmiControlManager { * @return {@link HdmiPlaybackClient} instance. {@code null} on failure. */ @Nullable + @SuppressLint("Doclava125") public HdmiPlaybackClient getPlaybackClient() { return (HdmiPlaybackClient) getClient(HdmiDeviceInfo.DEVICE_PLAYBACK); } @@ -333,6 +340,7 @@ public final class HdmiControlManager { * @return {@link HdmiTvClient} instance. {@code null} on failure. */ @Nullable + @SuppressLint("Doclava125") public HdmiTvClient getTvClient() { return (HdmiTvClient) getClient(HdmiDeviceInfo.DEVICE_TV); } @@ -343,6 +351,7 @@ public final class HdmiControlManager { * * @param isStandbyModeOn target status of the system's standby mode */ + @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setStandbyMode(boolean isStandbyModeOn) { try { mService.setStandbyMode(isStandbyModeOn); @@ -403,6 +412,7 @@ public final class HdmiControlManager { * @param listener {@link HotplugEventListener} instance * @see HdmiControlManager#removeHotplugEventListener(HotplugEventListener) */ + @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void addHotplugEventListener(HotplugEventListener listener) { if (mService == null) { Log.e(TAG, "HdmiControlService is not available"); @@ -426,6 +436,7 @@ public final class HdmiControlManager { * * @param listener {@link HotplugEventListener} instance to be removed */ + @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void removeHotplugEventListener(HotplugEventListener listener) { if (mService == null) { Log.e(TAG, "HdmiControlService is not available"); diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index 22b3638bdbb1..4898c1a0802f 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -19,6 +19,7 @@ package android.hardware.input; import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.SdkConstant; +import android.annotation.SystemService; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.media.AudioAttributes; @@ -53,13 +54,8 @@ import java.util.List; /** * Provides information about input devices and available key layouts. - * <p> - * Get an instance of this class by calling - * {@link android.content.Context#getSystemService(java.lang.String) - * Context.getSystemService()} with the argument - * {@link android.content.Context#INPUT_SERVICE}. - * </p> */ +@SystemService(Context.INPUT_SERVICE) public final class InputManager { private static final String TAG = "InputManager"; private static final boolean DEBUG = false; diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java index 7c4df473a9da..60500463d82e 100644 --- a/core/java/android/hardware/location/ContextHubManager.java +++ b/core/java/android/hardware/location/ContextHubManager.java @@ -15,7 +15,10 @@ */ package android.hardware.location; +import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.content.Context; import android.os.Handler; import android.os.Looper; @@ -33,6 +36,7 @@ import android.util.Log; * @hide */ @SystemApi +@SystemService(Context.CONTEXTHUB_SERVICE) public final class ContextHubManager { private static final String TAG = "ContextHubManager"; @@ -91,6 +95,7 @@ public final class ContextHubManager { * Get a handle to all the context hubs in the system * @return array of context hub handles */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public int[] getContextHubHandles() { try { return mService.getContextHubHandles(); @@ -107,6 +112,7 @@ public final class ContextHubManager { * * @see ContextHubInfo */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public ContextHubInfo getContextHubInfo(int hubHandle) { try { return mService.getContextHubInfo(hubHandle); @@ -134,6 +140,7 @@ public final class ContextHubManager { * * @see NanoApp */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public int loadNanoApp(int hubHandle, NanoApp app) { try { return mService.loadNanoApp(hubHandle, app); @@ -157,6 +164,7 @@ public final class ContextHubManager { * @return 0 if the command for unloading was sent to the context hub; * -1 otherwise */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public int unloadNanoApp(int nanoAppHandle) { try { return mService.unloadNanoApp(nanoAppHandle); @@ -191,6 +199,7 @@ public final class ContextHubManager { * * @see NanoAppInstanceInfo */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) { try { return mService.getNanoAppInstanceInfo(nanoAppHandle); @@ -209,6 +218,7 @@ public final class ContextHubManager { * * @return int[] Array of handles to any found nano apps */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public int[] findNanoAppOnHub(int hubHandle, NanoAppFilter filter) { try { return mService.findNanoAppOnHub(hubHandle, filter); @@ -236,6 +246,7 @@ public final class ContextHubManager { * * @return int 0 on success, -1 otherwise */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public int sendMessage(int hubHandle, int nanoAppHandle, ContextHubMessage message) { try { return mService.sendMessage(hubHandle, nanoAppHandle, message); @@ -253,6 +264,7 @@ public final class ContextHubManager { * * @return int 0 on success, -1 otherwise */ + @SuppressLint("Doclava125") public int registerCallback(Callback callback) { return registerCallback(callback, null); } @@ -281,6 +293,7 @@ public final class ContextHubManager { * * @return int 0 on success, -1 otherwise */ + @SuppressLint("Doclava125") public int registerCallback(Callback callback, Handler handler) { synchronized(this) { if (mCallback != null) { @@ -302,6 +315,7 @@ public final class ContextHubManager { * * @return int 0 on success, -1 otherwise */ + @SuppressLint("Doclava125") public int unregisterCallback(Callback callback) { synchronized(this) { if (callback != mCallback) { diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java index e659a3ecdc41..20292f7e7e48 100644 --- a/core/java/android/hardware/radio/RadioManager.java +++ b/core/java/android/hardware/radio/RadioManager.java @@ -19,6 +19,7 @@ package android.hardware.radio; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.content.Context; import android.os.Handler; import android.os.Parcel; @@ -41,6 +42,7 @@ import java.util.Arrays; * @hide */ @SystemApi +@SystemService(Context.RADIO_SERVICE) public class RadioManager { private static final String TAG = "RadioManager"; diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index a0fb82c64e4c..004530837206 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -19,6 +19,7 @@ package android.hardware.usb; import android.annotation.Nullable; import android.annotation.SdkConstant; +import android.annotation.SystemService; import android.annotation.SdkConstant.SdkConstantType; import android.app.PendingIntent; import android.content.ComponentName; @@ -38,18 +39,13 @@ import java.util.HashMap; * This class allows you to access the state of USB and communicate with USB devices. * Currently only host mode is supported in the public API. * - * <p>You can obtain an instance of this class by calling - * {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}. - * - * {@samplecode - * UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);} - * * <div class="special reference"> * <h3>Developer Guides</h3> * <p>For more information about communicating with USB hardware, read the * <a href="{@docRoot}guide/topics/connectivity/usb/index.html">USB developer guide</a>.</p> * </div> */ +@SystemService(Context.USB_SERVICE) public class UsbManager { private static final String TAG = "UsbManager"; diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index d2636ebb913e..fcb99b167d1e 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -21,6 +21,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; @@ -64,9 +65,7 @@ import java.util.Map; /** * Class that answers queries about the state of network connectivity. It also - * notifies applications when network connectivity changes. Get an instance - * of this class by calling - * {@link android.content.Context#getSystemService(String) Context.getSystemService(Context.CONNECTIVITY_SERVICE)}. + * notifies applications when network connectivity changes. * <p> * The primary responsibilities of this class are to: * <ol> @@ -80,6 +79,7 @@ import java.util.Map; * traffic</li> * </ol> */ +@SystemService(Context.CONNECTIVITY_SERVICE) public class ConnectivityManager { private static final String TAG = "ConnectivityManager"; @@ -2110,6 +2110,7 @@ public class ConnectivityManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int type, boolean showProvisioningUi, final OnStartTetheringCallback callback, Handler handler) { Preconditions.checkNotNull(callback, "OnStartTetheringCallback cannot be null."); @@ -2146,6 +2147,7 @@ public class ConnectivityManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int type) { try { String pkgName = mContext.getOpPackageName(); diff --git a/core/java/android/net/EthernetManager.java b/core/java/android/net/EthernetManager.java index 664b7b408975..31a30968cbcd 100644 --- a/core/java/android/net/EthernetManager.java +++ b/core/java/android/net/EthernetManager.java @@ -16,6 +16,7 @@ package android.net; +import android.annotation.SystemService; import android.content.Context; import android.net.IEthernetManager; import android.net.IEthernetServiceListener; @@ -31,6 +32,7 @@ import java.util.ArrayList; * * @hide */ +@SystemService(Context.ETHERNET_SERVICE) public class EthernetManager { private static final String TAG = "EthernetManager"; private static final int MSG_AVAILABILITY_CHANGED = 1000; diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index 0240cf15095e..2f791e151b08 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -18,6 +18,8 @@ package android.net; import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.NonNull; +import android.annotation.SystemService; +import android.content.Context; import android.os.Binder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -34,12 +36,9 @@ import java.net.Socket; * This class contains methods for managing IPsec sessions, which will perform kernel-space * encryption and decryption of socket or Network traffic. * - * <p>An IpSecManager may be obtained by calling {@link - * android.content.Context#getSystemService(String) Context#getSystemService(String)} with {@link - * android.content.Context#IPSEC_SERVICE Context#IPSEC_SERVICE} - * * @hide */ +@SystemService(Context.IPSEC_SERVICE) public final class IpSecManager { private static final String TAG = "IpSecManager"; diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index bf7207ca5bbb..2dd7f757aea3 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -418,10 +418,16 @@ public final class NetworkCapabilities implements Parcelable { */ public static final int TRANSPORT_WIFI_AWARE = 5; + /** + * Indicates this network uses a LoWPAN transport. + * @hide + */ + public static final int TRANSPORT_LOWPAN = 6; + /** @hide */ public static final int MIN_TRANSPORT = TRANSPORT_CELLULAR; /** @hide */ - public static final int MAX_TRANSPORT = TRANSPORT_WIFI_AWARE; + public static final int MAX_TRANSPORT = TRANSPORT_LOWPAN; private static final String[] TRANSPORT_NAMES = { "CELLULAR", @@ -429,7 +435,8 @@ public final class NetworkCapabilities implements Parcelable { "BLUETOOTH", "ETHERNET", "VPN", - "WIFI_AWARE" + "WIFI_AWARE", + "LOWPAN" }; /** diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 43fab037f254..4d94a55cd0c4 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -19,6 +19,7 @@ package android.net; import static android.content.pm.PackageManager.GET_SIGNATURES; import static android.net.NetworkPolicy.CYCLE_NONE; +import android.annotation.SystemService; import android.app.ActivityManager; import android.content.Context; import android.content.Intent; @@ -40,6 +41,7 @@ import java.util.TimeZone; * * {@hide} */ +@SystemService(Context.NETWORK_POLICY_SERVICE) public class NetworkPolicyManager { /* POLICY_* are masks and can be ORed, although currently they are not.*/ diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java index 9f6e45ca6fb5..7e0c9ce33b82 100644 --- a/core/java/android/net/NetworkScoreManager.java +++ b/core/java/android/net/NetworkScoreManager.java @@ -19,9 +19,11 @@ package android.net; import android.Manifest.permission; import android.annotation.IntDef; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.content.Context; import android.os.RemoteException; import android.os.ServiceManager; @@ -34,12 +36,6 @@ import java.util.List; /** * Class that manages communication between network subsystems and a network scorer. * - * <p>You can get an instance of this class by calling - * {@link android.content.Context#getSystemService(String)}: - * - * <pre>NetworkScoreManager manager = - * (NetworkScoreManager) getSystemService(Context.NETWORK_SCORE_SERVICE)</pre> - * * <p>A network scorer is any application which: * <ul> * <li>Declares the {@link permission#SCORE_NETWORKS} permission. @@ -51,6 +47,7 @@ import java.util.List; * @hide */ @SystemApi +@SystemService(Context.NETWORK_SCORE_SERVICE) public class NetworkScoreManager { /** * Activity action: ask the user to change the active network scorer. This will show a dialog @@ -243,6 +240,7 @@ public class NetworkScoreManager { * @hide */ @Nullable + @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES) public NetworkScorerAppData getActiveScorer() { try { return mService.getActiveScorer(); @@ -276,6 +274,7 @@ public class NetworkScoreManager { * @return whether the update was successful. * @throws SecurityException if the caller is not the active scorer. */ + @RequiresPermission(android.Manifest.permission.SCORE_NETWORKS) public boolean updateScores(ScoredNetwork[] networks) throws SecurityException { try { return mService.updateScores(networks); @@ -296,6 +295,7 @@ public class NetworkScoreManager { * @return whether the clear was successful. * @throws SecurityException if the caller is not the active scorer or privileged. */ + @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES) public boolean clearScores() throws SecurityException { try { return mService.clearScores(); @@ -316,6 +316,7 @@ public class NetworkScoreManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.SCORE_NETWORKS) public boolean setActiveScorer(String packageName) throws SecurityException { try { return mService.setActiveScorer(packageName); @@ -331,6 +332,7 @@ public class NetworkScoreManager { * * @throws SecurityException if the caller is neither the active scorer nor the system. */ + @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES) public void disableScoring() throws SecurityException { try { mService.disableScoring(); diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index fc66395bcd00..f9346165df3b 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -16,6 +16,7 @@ package android.net; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.app.DownloadManager; import android.app.backup.BackupManager; @@ -243,6 +244,7 @@ public class TrafficStats { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public static void setThreadStatsUid(int uid) { NetworkManagementSocketTagger.setThreadSocketStatsUid(uid); } @@ -255,6 +257,7 @@ public class TrafficStats { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public static void clearThreadStatsUid() { NetworkManagementSocketTagger.setThreadSocketStatsUid(-1); } diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java index c6daa15e3d62..2d9860cf0e40 100644 --- a/core/java/android/net/VpnService.java +++ b/core/java/android/net/VpnService.java @@ -19,6 +19,7 @@ package android.net; import static android.system.OsConstants.AF_INET; import static android.system.OsConstants.AF_INET6; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.app.Activity; import android.app.PendingIntent; @@ -177,6 +178,7 @@ public class VpnService extends Service { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.CONTROL_VPN) public static void prepareAndAuthorize(Context context) { IConnectivityManager cm = getService(); String packageName = context.getPackageName(); diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java index 22bda77195c0..ace37486647c 100644 --- a/core/java/android/net/nsd/NsdManager.java +++ b/core/java/android/net/nsd/NsdManager.java @@ -17,6 +17,7 @@ package android.net.nsd; import android.annotation.SdkConstant; +import android.annotation.SystemService; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.os.Handler; @@ -114,11 +115,9 @@ import com.android.internal.util.Protocol; * http://www.iana.org/form/ports-service. Existing services can be found at * http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml * - * Get an instance of this class by calling {@link android.content.Context#getSystemService(String) - * Context.getSystemService(Context.NSD_SERVICE)}. - * * {@see NsdServiceInfo} */ +@SystemService(Context.NSD_SERVICE) public final class NsdManager { private static final String TAG = NsdManager.class.getSimpleName(); private static final boolean DBG = false; diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index 783c25aa4d08..48869c71b685 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -18,6 +18,7 @@ package android.nfc; import java.util.HashMap; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; @@ -725,6 +726,7 @@ public final class NfcAdapter { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable() { try { return sService.enable(); @@ -753,6 +755,7 @@ public final class NfcAdapter { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable() { try { return sService.disable(true); @@ -767,6 +770,7 @@ public final class NfcAdapter { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable(boolean persist) { try { return sService.disable(persist); @@ -1552,6 +1556,7 @@ public final class NfcAdapter { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableNdefPush() { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); @@ -1570,6 +1575,7 @@ public final class NfcAdapter { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disableNdefPush() { synchronized (NfcAdapter.class) { if (!sHasNfcFeature) { @@ -1736,6 +1742,7 @@ public final class NfcAdapter { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean addNfcUnlockHandler(final NfcUnlockHandler unlockHandler, String[] tagTechnologies) { synchronized (NfcAdapter.class) { @@ -1785,6 +1792,7 @@ public final class NfcAdapter { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(NfcUnlockHandler unlockHandler) { synchronized (NfcAdapter.class) { if (!sHasNfcFeature) { diff --git a/core/java/android/nfc/NfcManager.java b/core/java/android/nfc/NfcManager.java index ea080140e3ac..50d674570c14 100644 --- a/core/java/android/nfc/NfcManager.java +++ b/core/java/android/nfc/NfcManager.java @@ -16,6 +16,7 @@ package android.nfc; +import android.annotation.SystemService; import android.content.Context; /** @@ -34,9 +35,9 @@ import android.content.Context; * <a href="{@docRoot}guide/topics/nfc/index.html">Near Field Communication</a> developer guide.</p> * </div> * - * @see Context#getSystemService * @see NfcAdapter#getDefaultAdapter(android.content.Context) */ +@SystemService(Context.NFC_SERVICE) public final class NfcManager { private final NfcAdapter mAdapter; diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java index 734d89eb72af..f715f507f6c9 100644 --- a/core/java/android/os/BatteryManager.java +++ b/core/java/android/os/BatteryManager.java @@ -16,6 +16,8 @@ package android.os; +import android.annotation.SystemService; +import android.content.Context; import android.hardware.health.V1_0.Constants; import com.android.internal.app.IBatteryStats; @@ -24,6 +26,7 @@ import com.android.internal.app.IBatteryStats; * in the {@link android.content.Intent#ACTION_BATTERY_CHANGED} Intent, and * provides a method for querying battery and charging properties. */ +@SystemService(Context.BATTERY_SERVICE) public class BatteryManager { /** * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index ecc4dec47af7..162dddb1f797 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -1219,6 +1219,7 @@ public abstract class BatteryStats implements Parcelable { // Platform-level low power state stats public String statPlatformIdleState; + public String statSubsystemPowerState; public HistoryStepDetails() { clear(); @@ -1250,6 +1251,7 @@ public abstract class BatteryStats implements Parcelable { out.writeInt(statSoftIrqTime); out.writeInt(statIdlTime); out.writeString(statPlatformIdleState); + out.writeString(statSubsystemPowerState); } public void readFromParcel(Parcel in) { @@ -1271,6 +1273,7 @@ public abstract class BatteryStats implements Parcelable { statSoftIrqTime = in.readInt(); statIdlTime = in.readInt(); statPlatformIdleState = in.readString(); + statSubsystemPowerState = in.readString(); } } @@ -5543,6 +5546,10 @@ public abstract class BatteryStats implements Parcelable { pw.print(", PlatformIdleStat "); pw.print(rec.stepDetails.statPlatformIdleState); pw.println(); + + pw.print(", SubsystemPowerState "); + pw.print(rec.stepDetails.statSubsystemPowerState); + pw.println(); } else { pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(','); pw.print(HISTORY_DATA); pw.print(",0,Dcpu="); @@ -5580,6 +5587,11 @@ public abstract class BatteryStats implements Parcelable { pw.print(rec.stepDetails.statPlatformIdleState); } pw.println(); + + if (rec.stepDetails.statSubsystemPowerState != null) { + pw.print(rec.stepDetails.statSubsystemPowerState); + } + pw.println(); } } oldState = rec.states; diff --git a/core/java/android/os/DropBoxManager.java b/core/java/android/os/DropBoxManager.java index db84b6fbf3c5..97f0e0cf31e9 100644 --- a/core/java/android/os/DropBoxManager.java +++ b/core/java/android/os/DropBoxManager.java @@ -17,6 +17,7 @@ package android.os; import android.annotation.SdkConstant; +import android.annotation.SystemService; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.util.Log; @@ -36,13 +37,10 @@ import java.util.zip.GZIPInputStream; * enqueued data exceeds the maximum size. You can think of this as a * persistent, system-wide, blob-oriented "logcat". * - * <p>You can obtain an instance of this class by calling - * {@link android.content.Context#getSystemService} - * with {@link android.content.Context#DROPBOX_SERVICE}. - * * <p>DropBoxManager entries are not sent anywhere directly, but other system * services and debugging tools may scan and upload entries for processing. */ +@SystemService(Context.DROPBOX_SERVICE) public class DropBoxManager { private static final String TAG = "DropBoxManager"; diff --git a/core/java/android/os/HardwarePropertiesManager.java b/core/java/android/os/HardwarePropertiesManager.java index 67edefc16bd0..aad202e77710 100644 --- a/core/java/android/os/HardwarePropertiesManager.java +++ b/core/java/android/os/HardwarePropertiesManager.java @@ -17,6 +17,7 @@ package android.os; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.SystemService; import android.content.Context; import android.hardware.thermal.V1_0.Constants; import android.util.Log; @@ -28,6 +29,7 @@ import java.lang.annotation.RetentionPolicy; * The HardwarePropertiesManager class provides a mechanism of accessing hardware state of a * device: CPU, GPU and battery temperatures, CPU usage per core, fan speed, etc. */ +@SystemService(Context.HARDWARE_PROPERTIES_SERVICE) public class HardwarePropertiesManager { private static final String TAG = HardwarePropertiesManager.class.getSimpleName(); diff --git a/core/java/android/os/IncidentManager.java b/core/java/android/os/IncidentManager.java index 976d59472e69..bc8e2e470c55 100644 --- a/core/java/android/os/IncidentManager.java +++ b/core/java/android/os/IncidentManager.java @@ -16,7 +16,9 @@ package android.os; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.annotation.TestApi; import android.content.Context; import android.os.IIncidentManager; @@ -31,6 +33,7 @@ import android.util.Slog; */ @SystemApi @TestApi +@SystemService(Context.INCIDENT_SERVICE) public class IncidentManager { private static final String TAG = "incident"; @@ -46,6 +49,10 @@ public class IncidentManager { /** * Take an incident report and put it in dropbox. */ + @RequiresPermission(allOf = { + android.Manifest.permission.DUMP, + android.Manifest.permission.PACKAGE_USAGE_STATS + }) public void reportIncident(IncidentReportArgs args) { final IIncidentManager service = IIncidentManager.Stub.asInterface( ServiceManager.getService("incident")); @@ -76,6 +83,10 @@ public class IncidentManager { * {@link android.util.proto.ProtoOutputStream#bytes bytes()} method to retrieve * the encoded data for the header. */ + @RequiresPermission(allOf = { + android.Manifest.permission.DUMP, + android.Manifest.permission.PACKAGE_USAGE_STATS + }) public void reportIncident(String settingName, byte[] headerProto) { // Sections String setting = Settings.System.getString(mContext.getContentResolver(), settingName); diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 7d1369fe1b69..a85ed9c0ce34 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -17,8 +17,10 @@ package android.os; import android.annotation.IntDef; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.content.Context; import android.util.Log; import java.lang.annotation.Retention; @@ -32,9 +34,6 @@ import java.lang.annotation.RetentionPolicy; * Do not acquire {@link WakeLock}s unless you really need them, use the minimum levels * possible, and be sure to release them as soon as possible. * </p><p> - * You can obtain an instance of this class by calling - * {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}. - * </p><p> * The primary API you'll use is {@link #newWakeLock(int, String) newWakeLock()}. * This will create a {@link PowerManager.WakeLock} object. You can then use methods * on the wake lock object to control the power state of the device. @@ -102,6 +101,7 @@ import java.lang.annotation.RetentionPolicy; * permission in an {@code <uses-permission>} element of the application's manifest. * </p> */ +@SystemService(Context.POWER_SERVICE) public final class PowerManager { private static final String TAG = "PowerManager"; @@ -689,6 +689,10 @@ public final class PowerManager { * @hide Requires signature or system permission. */ @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.DEVICE_POWER, + android.Manifest.permission.USER_ACTIVITY + }) public void userActivity(long when, int event, int flags) { try { mService.userActivity(when, event, flags); diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index cf8f3eb96621..db9f28b77288 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -18,7 +18,10 @@ package android.os; import static java.nio.charset.StandardCharsets.UTF_8; +import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -67,6 +70,7 @@ import sun.security.pkcs.SignerInfo; * recovery system (the separate partition that can be used to install * system updates, wipe user data, etc.) */ +@SystemService(Context.RECOVERY_SERVICE) public class RecoverySystem { private static final String TAG = "RecoverySystem"; @@ -387,6 +391,7 @@ public class RecoverySystem { * {@hide} */ @SystemApi + @SuppressLint("Doclava125") public static boolean verifyPackageCompatibility(File compatibilityFile) throws IOException { try (InputStream inputStream = new FileInputStream(compatibilityFile)) { return verifyPackageCompatibility(inputStream); @@ -409,6 +414,7 @@ public class RecoverySystem { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(Context context, File packageFile, final ProgressListener listener, @@ -469,6 +475,7 @@ public class RecoverySystem { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(Context context, File packageFile, final ProgressListener listener) @@ -490,6 +497,7 @@ public class RecoverySystem { * @throws IOException if writing the recovery command file * fails, or if the reboot itself fails. */ + @RequiresPermission(android.Manifest.permission.RECOVERY) public static void installPackage(Context context, File packageFile) throws IOException { installPackage(context, packageFile, false); @@ -511,6 +519,7 @@ public class RecoverySystem { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.RECOVERY) public static void installPackage(Context context, File packageFile, boolean processed) throws IOException { synchronized (sRequestLock) { @@ -602,6 +611,7 @@ public class RecoverySystem { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.RECOVERY) public static void scheduleUpdateOnBoot(Context context, File packageFile) throws IOException { String filename = packageFile.getCanonicalPath(); @@ -639,6 +649,7 @@ public class RecoverySystem { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.RECOVERY) public static void cancelScheduledUpdate(Context context) throws IOException { RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE); @@ -777,6 +788,10 @@ public class RecoverySystem { * @hide */ @SystemApi + @RequiresPermission(allOf = { + android.Manifest.permission.RECOVERY, + android.Manifest.permission.REBOOT + }) public static void rebootWipeAb(Context context, File packageFile, String reason) throws IOException { String reasonArg = null; diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 7e03e425a3db..3337def4c4e1 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -23,6 +23,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UserIdInt; import android.annotation.WorkerThread; @@ -63,6 +64,7 @@ import java.util.List; * <p> * See {@link DevicePolicyManager#ACTION_PROVISION_MANAGED_PROFILE} for more on managed profiles. */ +@SystemService(Context.USER_SERVICE) public class UserManager { private static final String TAG = "UserManager"; @@ -1035,12 +1037,12 @@ public class UserManager { /** * Checks if the calling app is running in a managed profile. - * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. * * @return whether the caller is in a managed profile. * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isManagedProfile() { // No need for synchronization. Once it becomes non-null, it'll be non-null forever. // Worst case we might end up calling the AIDL method multiple times but that's fine. @@ -1064,6 +1066,7 @@ public class UserManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isManagedProfile(@UserIdInt int userId) { if (userId == UserHandle.myUserId()) { return isManagedProfile(); @@ -1249,7 +1252,6 @@ public class UserManager { * @hide * * Returns who set a user restriction on a user. - * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. * @param restrictionKey the string key representing the restriction * @param userHandle the UserHandle of the user for whom to retrieve the restrictions. * @return The source of user restriction. Any combination of {@link #RESTRICTION_NOT_SET}, @@ -1260,6 +1262,7 @@ public class UserManager { @Deprecated @SystemApi @UserRestrictionSource + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public int getUserRestrictionSource(String restrictionKey, UserHandle userHandle) { try { return mService.getUserRestrictionSource(restrictionKey, userHandle.getIdentifier()); @@ -1272,12 +1275,12 @@ public class UserManager { * @hide * * Returns a list of users who set a user restriction on a given user. - * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. * @param restrictionKey the string key representing the restriction * @param userHandle the UserHandle of the user for whom to retrieve the restrictions. * @return a list of user ids enforcing this restriction. */ @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public List<EnforcingUser> getUserRestrictionSources( String restrictionKey, UserHandle userHandle) { try { @@ -1619,9 +1622,10 @@ public class UserManager { /** * @hide * - * Returns the preferred account name for user creation. Requires MANAGE_USERS permission. + * Returns the preferred account name for user creation. */ @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountName() { try { return mService.getSeedAccountName(); @@ -1633,9 +1637,10 @@ public class UserManager { /** * @hide * - * Returns the preferred account type for user creation. Requires MANAGE_USERS permission. + * Returns the preferred account type for user creation. */ @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountType() { try { return mService.getSeedAccountType(); @@ -1647,11 +1652,11 @@ public class UserManager { /** * @hide * - * Returns the preferred account's options bundle for user creation. Requires MANAGE_USERS - * permission. + * Returns the preferred account's options bundle for user creation. * @return Any options set by the requestor that created the user. */ @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public PersistableBundle getSeedAccountOptions() { try { return mService.getSeedAccountOptions(); @@ -1683,9 +1688,10 @@ public class UserManager { /** * @hide - * Clears the seed information used to create this user. Requires MANAGE_USERS permission. + * Clears the seed information used to create this user. */ @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void clearSeedAccountData() { try { mService.clearSeedAccountData(); @@ -1768,13 +1774,13 @@ public class UserManager { /** * Returns serial numbers of all users on this device. - * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. * * @param excludeDying specify if the list should exclude users being removed. * @return the list of serial numbers of users that exist on the device. * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public long[] getSerialNumbersOfUsers(boolean excludeDying) { try { List<UserInfo> users = mService.getUsers(excludeDying); diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index 2f0eecae2c96..8078fb87eb27 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -17,6 +17,7 @@ package android.os; import android.annotation.RequiresPermission; +import android.annotation.SystemService; import android.app.ActivityThread; import android.content.Context; import android.media.AudioAttributes; @@ -27,10 +28,8 @@ import android.util.Log; * <p> * If your process exits, any vibration you started will stop. * </p> - * - * To obtain an instance of the system vibrator, call - * {@link Context#getSystemService} with {@link Context#VIBRATOR_SERVICE} as the argument. */ +@SystemService(Context.VIBRATOR_SERVICE) public abstract class Vibrator { private static final String TAG = "Vibrator"; diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java index 7c0af2508a93..bba4cd1fb8e4 100644 --- a/core/java/android/os/health/SystemHealthManager.java +++ b/core/java/android/os/health/SystemHealthManager.java @@ -16,6 +16,7 @@ package android.os.health; +import android.annotation.SystemService; import android.content.Context; import android.os.BatteryStats; import android.os.Process; @@ -40,6 +41,7 @@ import com.android.internal.app.IBatteryStats; * JobScheduler}), and while that can affect charging rates, it is still preferable * to actually draining the battery. */ +@SystemService(Context.SYSTEM_HEALTH_SERVICE) public class SystemHealthManager { private final IBatteryStats mBatteryStats; diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index d81ee4ef9843..a6cdb03c8b67 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -25,7 +25,9 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; +import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.annotation.WorkerThread; import android.app.ActivityThread; import android.content.ContentResolver; @@ -98,11 +100,8 @@ import java.util.concurrent.atomic.AtomicInteger; * guarantee the security of the OBB file itself: if any program modifies the * OBB, there is no guarantee that a read from that OBB will produce the * expected output. - * <p> - * Get an instance of this class by calling - * {@link android.content.Context#getSystemService(java.lang.String)} with an - * argument of {@link android.content.Context#STORAGE_SERVICE}. */ +@SystemService(Context.STORAGE_SERVICE) public class StorageManager { private static final String TAG = "StorageManager"; @@ -1701,8 +1700,9 @@ public class StorageManager { /** @hide */ @SystemApi - public long getAllocatableBytes(@NonNull UUID storageUuid, @AllocateFlags int flags) - throws IOException { + @SuppressLint("Doclava125") + public long getAllocatableBytes(@NonNull UUID storageUuid, + @RequiresPermission @AllocateFlags int flags) throws IOException { try { return mStorageManager.getAllocatableBytes(convert(storageUuid), flags); } catch (ParcelableException e) { @@ -1715,8 +1715,9 @@ public class StorageManager { /** @removed */ @Deprecated - public long getAllocatableBytes(@NonNull File path, @AllocateFlags int flags) - throws IOException { + @SuppressLint("Doclava125") + public long getAllocatableBytes(@NonNull File path, + @RequiresPermission @AllocateFlags int flags) throws IOException { return getAllocatableBytes(getUuidForPath(path), flags); } @@ -1749,8 +1750,9 @@ public class StorageManager { /** @hide */ @SystemApi + @SuppressLint("Doclava125") public void allocateBytes(@NonNull UUID storageUuid, @BytesLong long bytes, - @AllocateFlags int flags) throws IOException { + @RequiresPermission @AllocateFlags int flags) throws IOException { try { mStorageManager.allocateBytes(convert(storageUuid), bytes, flags); } catch (ParcelableException e) { @@ -1762,8 +1764,9 @@ public class StorageManager { /** @removed */ @Deprecated - public void allocateBytes(@NonNull File path, @BytesLong long bytes, @AllocateFlags int flags) - throws IOException { + @SuppressLint("Doclava125") + public void allocateBytes(@NonNull File path, @BytesLong long bytes, + @RequiresPermission @AllocateFlags int flags) throws IOException { allocateBytes(getUuidForPath(path), bytes, flags); } @@ -1798,8 +1801,9 @@ public class StorageManager { /** @hide */ @SystemApi - public void allocateBytes(FileDescriptor fd, @BytesLong long bytes, @AllocateFlags int flags) - throws IOException { + @SuppressLint("Doclava125") + public void allocateBytes(FileDescriptor fd, @BytesLong long bytes, + @RequiresPermission @AllocateFlags int flags) throws IOException { final File file = ParcelFileDescriptor.getFile(fd); for (int i = 0; i < 3; i++) { try { diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java index 8ee05177f186..e8ff2e2cee98 100644 --- a/core/java/android/print/PrintManager.java +++ b/core/java/android/print/PrintManager.java @@ -19,6 +19,7 @@ package android.print; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.app.Activity; import android.app.Application.ActivityLifecycleCallbacks; import android.content.ComponentName; @@ -57,14 +58,6 @@ import java.util.Map; /** * System level service for accessing the printing capabilities of the platform. - * <p> - * To obtain a handle to the print manager do the following: - * </p> - * - * <pre> - * PrintManager printManager = - * (PrintManager) context.getSystemService(Context.PRINT_SERVICE); - * </pre> * * <h3>Print mechanics</h3> * <p> @@ -109,6 +102,7 @@ import java.util.Map; * @see PrintJob * @see PrintJobInfo */ +@SystemService(Context.PRINT_SERVICE) public final class PrintManager { private static final String LOG_TAG = "PrintManager"; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 172ba89bb3bf..e0abfb56c1b0 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6781,6 +6781,13 @@ public final class Settings { public static final String CAMERA_LIFT_TRIGGER_ENABLED = "camera_lift_trigger_enabled"; /** + * The default enable state of the camera lift trigger. + * + * @hide + */ + public static final int CAMERA_LIFT_TRIGGER_ENABLED_DEFAULT = 1; + + /** * Whether the assist gesture should be enabled. * * @hide diff --git a/core/java/android/service/autofill/FillContext.java b/core/java/android/service/autofill/FillContext.java index 251d346efb42..6956c8ac7135 100644 --- a/core/java/android/service/autofill/FillContext.java +++ b/core/java/android/service/autofill/FillContext.java @@ -114,7 +114,7 @@ public final class FillContext implements Parcelable { * * @hide */ - @NonNull public ViewNode[] findViewNodesByAutofillIds(@NonNull AutofillId[] ids) { + @NonNull public ViewNode[] findViewNodesByAutofillIds(@NonNull AutofillId... ids) { final LinkedList<ViewNode> nodesToProcess = new LinkedList<>(); final ViewNode[] foundNodes = new AssistStructure.ViewNode[ids.length]; diff --git a/core/java/android/service/oemlock/OemLockManager.java b/core/java/android/service/oemlock/OemLockManager.java index 644ca6c43506..3a56d9f1bf4f 100644 --- a/core/java/android/service/oemlock/OemLockManager.java +++ b/core/java/android/service/oemlock/OemLockManager.java @@ -17,7 +17,10 @@ package android.service.oemlock; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.content.Context; import android.os.RemoteException; /** @@ -31,6 +34,7 @@ import android.os.RemoteException; * @hide */ @SystemApi +@SystemService(Context.OEM_LOCK_SERVICE) public class OemLockManager { private IOemLockService mService; @@ -55,6 +59,7 @@ public class OemLockManager { * * @see #isOemUnlockAllowedByCarrier() */ + @RequiresPermission(android.Manifest.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE) public void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature) { try { mService.setOemUnlockAllowedByCarrier(allowed, signature); @@ -69,6 +74,7 @@ public class OemLockManager { * * @see #setOemUnlockAllowedByCarrier(boolean, byte[]) */ + @RequiresPermission(android.Manifest.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE) public boolean isOemUnlockAllowedByCarrier() { try { return mService.isOemUnlockAllowedByCarrier(); @@ -87,6 +93,7 @@ public class OemLockManager { * * @see #isOemUnlockAllowedByUser() */ + @RequiresPermission(android.Manifest.permission.MANAGE_USER_OEM_UNLOCK_STATE) public void setOemUnlockAllowedByUser(boolean allowed) { try { mService.setOemUnlockAllowedByUser(allowed); @@ -101,6 +108,7 @@ public class OemLockManager { * * @see #setOemUnlockAllowedByUser(boolean) */ + @RequiresPermission(android.Manifest.permission.MANAGE_USER_OEM_UNLOCK_STATE) public boolean isOemUnlockAllowedByUser() { try { return mService.isOemUnlockAllowedByUser(); diff --git a/core/java/android/service/persistentdata/PersistentDataBlockManager.java b/core/java/android/service/persistentdata/PersistentDataBlockManager.java index 326796afb3ad..fa75ad33d250 100644 --- a/core/java/android/service/persistentdata/PersistentDataBlockManager.java +++ b/core/java/android/service/persistentdata/PersistentDataBlockManager.java @@ -16,10 +16,14 @@ package android.service.persistentdata; -import android.annotation.SystemApi; import android.annotation.IntDef; +import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.content.Context; import android.os.RemoteException; -import android.util.Slog; +import android.service.oemlock.OemLockManager; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -43,6 +47,7 @@ import java.lang.annotation.RetentionPolicy; * @hide */ @SystemApi +@SystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE) public class PersistentDataBlockManager { private static final String TAG = PersistentDataBlockManager.class.getSimpleName(); private IPersistentDataBlockService sService; @@ -85,6 +90,7 @@ public class PersistentDataBlockManager { * * @param data the data to write */ + @SuppressLint("Doclava125") public int write(byte[] data) { try { return sService.write(data); @@ -96,6 +102,7 @@ public class PersistentDataBlockManager { /** * Returns the data block stored on the persistent partition. */ + @SuppressLint("Doclava125") public byte[] read() { try { return sService.read(); @@ -109,6 +116,7 @@ public class PersistentDataBlockManager { * * Return -1 on error. */ + @RequiresPermission(android.Manifest.permission.ACCESS_PDB_STATE) public int getDataBlockSize() { try { return sService.getDataBlockSize(); @@ -136,6 +144,7 @@ public class PersistentDataBlockManager { * It will also prevent any further {@link #write} operation until reboot, * in order to prevent a potential race condition. See b/30352311. */ + @RequiresPermission(android.Manifest.permission.OEM_UNLOCK_STATE) public void wipe() { try { sService.wipe(); @@ -149,6 +158,7 @@ public class PersistentDataBlockManager { * * @deprecated use {@link OemLockManager#setOemUnlockAllowedByUser(boolean)} instead. */ + @RequiresPermission(android.Manifest.permission.OEM_UNLOCK_STATE) public void setOemUnlockEnabled(boolean enabled) { try { sService.setOemUnlockEnabled(enabled); @@ -162,6 +172,10 @@ public class PersistentDataBlockManager { * * @deprecated use {@link OemLockManager#isOemUnlockAllowedByUser()} instead. */ + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_OEM_UNLOCK_STATE, + android.Manifest.permission.OEM_UNLOCK_STATE + }) public boolean getOemUnlockEnabled() { try { return sService.getOemUnlockEnabled(); @@ -177,6 +191,10 @@ public class PersistentDataBlockManager { * {@link #FLASH_LOCK_UNLOCKED} if device bootloader is unlocked, or {@link #FLASH_LOCK_UNKNOWN} * if this information cannot be ascertained on this device. */ + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_OEM_UNLOCK_STATE, + android.Manifest.permission.OEM_UNLOCK_STATE + }) @FlashLockState public int getFlashLockState() { try { diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java index 2f87d2e815cb..31cece49c79d 100644 --- a/core/java/android/view/HapticFeedbackConstants.java +++ b/core/java/android/view/HapticFeedbackConstants.java @@ -29,12 +29,12 @@ public class HapticFeedbackConstants { * in an action being performed. */ public static final int LONG_PRESS = 0; - + /** * The user has pressed on a virtual on-screen key. */ public static final int VIRTUAL_KEY = 1; - + /** * The user has pressed a soft keyboard key. */ @@ -57,24 +57,29 @@ public class HapticFeedbackConstants { public static final int CONTEXT_CLICK = 6; /** + * The user has released a virtual or software keyboard key. + */ + public static final int VIRTUAL_KEY_RELEASE = 7; + + /** * This is a private constant. Feel free to renumber as desired. * @hide */ public static final int SAFE_MODE_DISABLED = 10000; - + /** * This is a private constant. Feel free to renumber as desired. * @hide */ public static final int SAFE_MODE_ENABLED = 10001; - + /** * Flag for {@link View#performHapticFeedback(int, int) * View.performHapticFeedback(int, int)}: Ignore the setting in the * view for whether to perform haptic feedback, do it always. */ public static final int FLAG_IGNORE_VIEW_SETTING = 0x0001; - + /** * Flag for {@link View#performHapticFeedback(int, int) * View.performHapticFeedback(int, int)}: Ignore the global setting diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 51d65144f260..286e79055448 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -96,7 +96,7 @@ interface IWindowSession { int flags, out Rect outFrame, out Rect outOverscanInsets, out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets, out Rect outOutsets, out Rect outBackdropFrame, - out MergedConfiguration outMergedConfiguration, out Surface outSurface); + inout MergedConfiguration mergedConfiguration, out Surface outSurface); /* * Notify the window manager that an application is relaunching and diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index ad46d07d61c9..47b8d921da2e 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -18,6 +18,7 @@ package android.view; import android.annotation.LayoutRes; import android.annotation.Nullable; +import android.annotation.SystemService; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; @@ -47,10 +48,7 @@ import java.util.HashMap; * {@link android.app.Activity#getLayoutInflater()} or * {@link Context#getSystemService} to retrieve a standard LayoutInflater instance * that is already hooked up to the current context and correctly configured - * for the device you are running on. For example: - * - * <pre>LayoutInflater inflater = (LayoutInflater)context.getSystemService - * (Context.LAYOUT_INFLATER_SERVICE);</pre> + * for the device you are running on. * * <p> * To create a new LayoutInflater with an additional {@link Factory} for your @@ -64,9 +62,8 @@ import java.util.HashMap; * to use LayoutInflater with an XmlPullParser over a plain XML file at runtime; * it only works with an XmlPullParser returned from a compiled resource * (R.<em>something</em> file.) - * - * @see Context#getSystemService */ +@SystemService(Context.LAYOUT_INFLATER_SERVICE) public abstract class LayoutInflater { private static final String TAG = LayoutInflater.class.getSimpleName(); diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 8bb3fa988a45..0ad591be2cf8 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -129,11 +129,17 @@ public class Surface implements Parcelable { public static final int SCALING_MODE_NO_SCALE_CROP = 3; /** @hide */ - @IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270}) + @IntDef({ROTATION_UNDEFINED, ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270}) @Retention(RetentionPolicy.SOURCE) public @interface Rotation {} /** + * Rotation constant: undefined + * @hide + */ + public static final int ROTATION_UNDEFINED = -1; + + /** * Rotation constant: 0 degree rotation (natural orientation) */ public static final int ROTATION_0 = 0; diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index b57ac66e10cc..ef78559e2b53 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -31,6 +31,7 @@ import android.graphics.Rect; import android.graphics.Region; import android.os.Build; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.SystemClock; import android.util.AttributeSet; @@ -452,6 +453,14 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb } } + private void updateOpaqueFlag() { + if (!PixelFormat.formatHasAlpha(mRequestedFormat)) { + mSurfaceFlags |= SurfaceControl.OPAQUE; + } else { + mSurfaceFlags &= ~SurfaceControl.OPAQUE; + } + } + private Rect getParentSurfaceInsets() { final ViewRootImpl root = getViewRootImpl(); if (root == null) { @@ -522,7 +531,9 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb if (creating) { mSurfaceSession = new SurfaceSession(viewRoot.mSurface); mDeferredDestroySurfaceControl = mSurfaceControl; - mSurfaceControl = new SurfaceControl(mSurfaceSession, + + updateOpaqueFlag(); + mSurfaceControl = new SurfaceControlWithBackground(mSurfaceSession, "SurfaceView - " + viewRoot.getTitle().toString(), mSurfaceWidth, mSurfaceHeight, mFormat, mSurfaceFlags); @@ -1071,4 +1082,126 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb return mSurfaceFrame; } }; + + class SurfaceControlWithBackground extends SurfaceControl { + private SurfaceControl mBackgroundControl; + private boolean mOpaque = true; + public boolean mVisible = false; + + public SurfaceControlWithBackground(SurfaceSession s, + String name, int w, int h, int format, int flags) + throws Exception { + super(s, name, w, h, format, flags); + mBackgroundControl = new SurfaceControl(s, "Background for - " + name, w, h, + PixelFormat.OPAQUE, flags | SurfaceControl.FX_SURFACE_DIM); + mOpaque = (flags & SurfaceControl.OPAQUE) != 0; + } + + @Override + public void setAlpha(float alpha) { + super.setAlpha(alpha); + mBackgroundControl.setAlpha(alpha); + } + + @Override + public void setLayer(int zorder) { + super.setLayer(zorder); + // -3 is below all other child layers as SurfaceView never goes below -2 + mBackgroundControl.setLayer(-3); + } + + @Override + public void setPosition(float x, float y) { + super.setPosition(x, y); + mBackgroundControl.setPosition(x, y); + } + + @Override + public void setSize(int w, int h) { + super.setSize(w, h); + mBackgroundControl.setSize(w, h); + } + + @Override + public void setWindowCrop(Rect crop) { + super.setWindowCrop(crop); + mBackgroundControl.setWindowCrop(crop); + } + + @Override + public void setFinalCrop(Rect crop) { + super.setFinalCrop(crop); + mBackgroundControl.setFinalCrop(crop); + } + + @Override + public void setLayerStack(int layerStack) { + super.setLayerStack(layerStack); + mBackgroundControl.setLayerStack(layerStack); + } + + @Override + public void setOpaque(boolean isOpaque) { + super.setOpaque(isOpaque); + mOpaque = isOpaque; + updateBackgroundVisibility(); + } + + @Override + public void setSecure(boolean isSecure) { + super.setSecure(isSecure); + } + + @Override + public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { + super.setMatrix(dsdx, dtdx, dsdy, dtdy); + mBackgroundControl.setMatrix(dsdx, dtdx, dsdy, dtdy); + } + + @Override + public void hide() { + super.hide(); + mVisible = false; + updateBackgroundVisibility(); + } + + @Override + public void show() { + super.show(); + mVisible = true; + updateBackgroundVisibility(); + } + + @Override + public void destroy() { + super.destroy(); + mBackgroundControl.destroy(); + } + + @Override + public void release() { + super.release(); + mBackgroundControl.release(); + } + + @Override + public void setTransparentRegionHint(Region region) { + super.setTransparentRegionHint(region); + mBackgroundControl.setTransparentRegionHint(region); + } + + @Override + public void deferTransactionUntil(IBinder handle, long frame) { + super.deferTransactionUntil(handle, frame); + mBackgroundControl.deferTransactionUntil(handle, frame); + } + + void updateBackgroundVisibility() { + if (mOpaque && mVisible) { + mBackgroundControl.show(); + } else { + mBackgroundControl.hide(); + } + } + } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 10de5831de6f..2e817eb91cc4 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -4937,9 +4937,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager needGlobalAttributesUpdate(true); } ai.mKeepScreenOn = lastKeepOn; - if (!childHasFocus && child.hasFocusable()) { - focusableViewAvailable(child); - } } if (child.isLayoutDirectionInherited()) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 4a231efcdd07..be0bd840c8fa 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1850,8 +1850,12 @@ public final class ViewRootImpl implements ViewParent, final boolean isViewVisible = viewVisibility == View.VISIBLE; final boolean windowRelayoutWasForced = mForceNextWindowRelayout; - if (mFirst || windowShouldResize || insetsChanged || - viewVisibilityChanged || params != null || mForceNextWindowRelayout) { + final int contextConfigSeq = mContext.getResources().getConfiguration().seq; + final int lastConfigSeq = mLastReportedMergedConfiguration.getMergedConfiguration().seq; + final boolean staleConfig = lastConfigSeq != 0 && contextConfigSeq != lastConfigSeq; + + if (mFirst || windowShouldResize || insetsChanged || staleConfig || viewVisibilityChanged + || params != null || mForceNextWindowRelayout) { mForceNextWindowRelayout = false; if (isViewVisible) { @@ -6088,7 +6092,13 @@ public final class ViewRootImpl implements ViewParent, if (params != null) { if (DBG) Log.d(mTag, "WindowLayout in layoutWindow:" + params); } - mPendingMergedConfiguration.getMergedConfiguration().seq = 0; + + if (mPendingMergedConfiguration.getMergedConfiguration().seq == 0) { + mPendingMergedConfiguration.setTo(mLastReportedMergedConfiguration); + } + + int initialConfigSeq = mPendingMergedConfiguration.getMergedConfiguration().seq; + //Log.d(mTag, ">>>>>> CALLING relayout"); if (params != null && mOrigWindowType != params.type) { // For compatibility with old apps, don't crash here. @@ -6107,6 +6117,10 @@ public final class ViewRootImpl implements ViewParent, mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingMergedConfiguration, mSurface); + if (initialConfigSeq == mPendingMergedConfiguration.getMergedConfiguration().seq) { + mPendingMergedConfiguration.getMergedConfiguration().seq = 0; + } + mPendingAlwaysConsumeNavBar = (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0; diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 12ff21788da5..dfbf9620c82c 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -19,6 +19,7 @@ package android.view; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.annotation.TestApi; import android.app.KeyguardManager; import android.app.Presentation; @@ -39,8 +40,6 @@ import java.util.Objects; /** * The interface that apps use to talk to the window manager. - * <p> - * Use <code>Context.getSystemService(Context.WINDOW_SERVICE)</code> to get one of these. * </p><p> * Each window manager instance is bound to a particular {@link Display}. * To obtain a {@link WindowManager} for a different display, use @@ -52,10 +51,8 @@ import java.util.Objects; * {@link Presentation}. The presentation will automatically obtain a * {@link WindowManager} and {@link Context} for that display. * </p> - * - * @see android.content.Context#getSystemService - * @see android.content.Context#WINDOW_SERVICE */ +@SystemService(Context.WINDOW_SERVICE) public interface WindowManager extends ViewManager { /** @hide */ diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index c8f297ae9c1f..8fc586eba21d 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -24,6 +24,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; @@ -60,15 +61,6 @@ import java.util.List; * {@link android.view.View} changes etc. Parties interested in handling accessibility * events implement and register an accessibility service which extends * {@link android.accessibilityservice.AccessibilityService}. - * <p> - * To obtain a handle to the accessibility manager do the following: - * </p> - * <p> - * <code> - * <pre>AccessibilityManager accessibilityManager = - * (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);</pre> - * </code> - * </p> * * @see AccessibilityEvent * @see AccessibilityNodeInfo @@ -76,6 +68,7 @@ import java.util.List; * @see Context#getSystemService * @see Context#ACCESSIBILITY_SERVICE */ +@SystemService(Context.ACCESSIBILITY_SERVICE) public final class AccessibilityManager { private static final boolean DEBUG = false; diff --git a/core/java/android/view/accessibility/CaptioningManager.java b/core/java/android/view/accessibility/CaptioningManager.java index 14f0b0a06218..d6455e7270a9 100644 --- a/core/java/android/view/accessibility/CaptioningManager.java +++ b/core/java/android/view/accessibility/CaptioningManager.java @@ -18,6 +18,7 @@ package android.view.accessibility; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemService; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; @@ -34,14 +35,8 @@ import java.util.Locale; /** * Contains methods for accessing and monitoring preferred video captioning state and visual * properties. - * <p> - * To obtain a handle to the captioning manager, do the following: - * <p> - * <code> - * <pre>CaptioningManager captioningManager = - * (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE);</pre> - * </code> */ +@SystemService(Context.CAPTIONING_SERVICE) public class CaptioningManager { /** Default captioning enabled value. */ private static final int DEFAULT_ENABLED = 0; diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 02ecc501ea39..310ec1c938d0 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -23,6 +23,7 @@ import static android.view.autofill.Helper.sVerbose; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemService; import android.content.Context; import android.content.Intent; import android.content.IntentSender; @@ -55,6 +56,7 @@ import java.util.Objects; * * <p>It is safe to call into this from any thread. */ +@SystemService(Context.AUTOFILL_MANAGER_SERVICE) public final class AutofillManager { private static final String TAG = "AutofillManager"; diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index da9316c300b3..e2f7979c61d9 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -21,6 +21,7 @@ import static android.Manifest.permission.WRITE_SECURE_SETTINGS; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SystemService; import android.content.Context; import android.graphics.Rect; import android.net.Uri; @@ -73,8 +74,6 @@ import java.util.concurrent.TimeUnit; /** * Central system API to the overall input method framework (IMF) architecture, * which arbitrates interaction between applications and the current input method. - * You can retrieve an instance of this interface with - * {@link Context#getSystemService(String) Context.getSystemService()}. * * <p>Topics covered here: * <ol> @@ -211,6 +210,7 @@ import java.util.concurrent.TimeUnit; * and want to make it available for use.</p> * </ul> */ +@SystemService(Context.INPUT_METHOD_SERVICE) public final class InputMethodManager { static final boolean DEBUG = false; static final String TAG = "InputMethodManager"; diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java index 6b641dbfef8e..efc88e23fa67 100644 --- a/core/java/android/view/textclassifier/TextClassificationManager.java +++ b/core/java/android/view/textclassifier/TextClassificationManager.java @@ -18,6 +18,7 @@ package android.view.textclassifier; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemService; import android.content.Context; import android.os.ParcelFileDescriptor; import android.util.Log; @@ -33,10 +34,8 @@ import java.util.Locale; /** * Interface to the text classification service. - * - * <p>You do not instantiate this class directly; instead, retrieve it through - * {@link android.content.Context#getSystemService}. */ +@SystemService(Context.TEXT_CLASSIFICATION_SERVICE) public final class TextClassificationManager { private static final String LOG_TAG = "TextClassificationManager"; diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java index b4e6c5699007..d9bfade33539 100644 --- a/core/java/android/view/textservice/TextServicesManager.java +++ b/core/java/android/view/textservice/TextServicesManager.java @@ -16,6 +16,7 @@ package android.view.textservice; +import android.annotation.SystemService; import android.content.Context; import android.os.Bundle; import android.os.RemoteException; @@ -30,8 +31,7 @@ import java.util.Locale; /** * System API to the overall text services, which arbitrates interaction between applications - * and text services. You can retrieve an instance of this interface with - * {@link Context#getSystemService(String) Context.getSystemService()}. + * and text services. * * The user can change the current text services in Settings. And also applications can specify * the target text services. @@ -61,6 +61,7 @@ import java.util.Locale; * </ul> * */ +@SystemService(Context.TEXT_SERVICES_MANAGER_SERVICE) public final class TextServicesManager { private static final String TAG = TextServicesManager.class.getSimpleName(); private static final boolean DBG = false; diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 1fef7cbd1953..c19ee56e7636 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -3842,11 +3842,12 @@ public class Editor { } if (mTextView.canRequestAutofill()) { - final int mode = mTextView.getText().length() <= 0 - ? MenuItem.SHOW_AS_ACTION_IF_ROOM : MenuItem.SHOW_AS_ACTION_NEVER; - menu.add(Menu.NONE, TextView.ID_AUTOFILL, MENU_ITEM_ORDER_AUTOFILL, - com.android.internal.R.string.autofill) - .setShowAsAction(mode); + final String selected = mTextView.getSelectedText(); + if (selected == null || selected.isEmpty()) { + menu.add(Menu.NONE, TextView.ID_AUTOFILL, MENU_ITEM_ORDER_AUTOFILL, + com.android.internal.R.string.autofill) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + } } if (mTextView.canPasteAsPlainText()) { diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java index 797cf2b6de56..d327180c6e9d 100644 --- a/core/java/com/android/internal/notification/SystemNotificationChannels.java +++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java @@ -135,7 +135,7 @@ public class SystemNotificationChannels { channelsList.add(new NotificationChannel( FOREGROUND_SERVICE, context.getString(R.string.notification_channel_foreground_service), - NotificationManager.IMPORTANCE_MIN)); + NotificationManager.IMPORTANCE_LOW)); nm.createNotificationChannels(channelsList); } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 1b0d3323b3d0..cbe4c15cf790 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -171,6 +171,7 @@ public class BatteryStatsImpl extends BatteryStats { public interface PlatformIdleStateCallback { public String getPlatformLowPowerStats(); + public String getSubsystemLowPowerStats(); } private final PlatformIdleStateCallback mPlatformIdleStateCallback; @@ -2878,6 +2879,12 @@ public class BatteryStatsImpl extends BatteryStats { mPlatformIdleStateCallback.getPlatformLowPowerStats(); if (DEBUG) Slog.i(TAG, "WRITE PlatformIdleState:" + mCurHistoryStepDetails.statPlatformIdleState); + + mCurHistoryStepDetails.statSubsystemPowerState = + mPlatformIdleStateCallback.getSubsystemLowPowerStats(); + if (DEBUG) Slog.i(TAG, "WRITE SubsystemPowerState:" + + mCurHistoryStepDetails.statSubsystemPowerState); + } computeHistoryStepDetails(mCurHistoryStepDetails, mLastHistoryStepDetails); if (includeStepDetails != 0) { @@ -10175,7 +10182,16 @@ public class BatteryStatsImpl extends BatteryStats { new KernelUidCpuTimeReader.Callback() { @Override public void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs) { - final Uid u = getUidStatsLocked(mapUid(uid)); + uid = mapUid(uid); + if (Process.isIsolated(uid)) { + // This could happen if the isolated uid mapping was removed before + // that process was actually killed. + mKernelUidCpuTimeReader.removeUid(uid); + Slog.d(TAG, "Got readings for an isolated uid with" + + " no mapping to owning uid: " + uid); + return; + } + final Uid u = getUidStatsLocked(uid); // Accumulate the total system and user time. mTempTotalCpuUserTimeUs += userTimeUs; @@ -10340,7 +10356,11 @@ public class BatteryStatsImpl extends BatteryStats { @Override public void onUidCpuFreqTime(int uid, long[] cpuFreqTimeMs) { - final Uid u = getUidStatsLocked(mapUid(uid)); + uid = mapUid(uid); + if (Process.isIsolated(uid)) { + return; + } + final Uid u = getUidStatsLocked(uid); if (u.mCpuFreqTimeMs == null) { u.mCpuFreqTimeMs = new LongSamplingCounterArray(mOnBatteryTimeBase); } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 46bdafab9700..ae7355f77cd0 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1322,6 +1322,27 @@ <permission android:name="android.permission.NETWORK_SETTINGS" android:protectionLevel="signature" /> + <!-- #SystemApi @hide Allows applications to access information about LoWPAN interfaces. + <p>Not for use by third-party applications. --> + <permission android:name="android.permission.ACCESS_LOWPAN_STATE" + android:protectionLevel="signature|privileged" /> + + <!-- #SystemApi @hide Allows applications to change LoWPAN connectivity state. + <p>Not for use by third-party applications. --> + <permission android:name="android.permission.CHANGE_LOWPAN_STATE" + android:protectionLevel="signature|privileged" /> + + <!-- #SystemApi @hide Allows applications to read LoWPAN credential. + <p>Not for use by third-party applications. --> + <permission android:name="android.permission.READ_LOWPAN_CREDENTIAL" + android:protectionLevel="signature|privileged" /> + + <!-- #SystemApi @hide Allows a service to register or unregister + new LoWPAN interfaces. + <p>Not for use by third-party applications. --> + <permission android:name="android.permission.MANAGE_LOWPAN_INTERFACES" + android:protectionLevel="signature|privileged" /> + <!-- ======================================= --> <!-- Permissions for short range, peripheral networks --> <!-- ======================================= --> diff --git a/core/res/res/drawable/stat_sys_vitals.xml b/core/res/res/drawable/stat_sys_vitals.xml new file mode 100644 index 000000000000..213dd5fbed6e --- /dev/null +++ b/core/res/res/drawable/stat_sys_vitals.xml @@ -0,0 +1,29 @@ +<!-- +Copyright (C) 2017 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. +--> +<!-- "system vitals", as represented by an EKG trace --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M19.5608645,12.0797103 L17.15,5.15 L15.25,5.15 L11.95,15.95 L9.75,11.5 L7.95,11.55 L7.2,13.3 + L6.65,14.6 L3.25,14.6 L3.25,16.6 L7.35,16.6 L8,16.6 L8.25,16 L8.9,14.3 L11.2,18.85 L13.15,18.85 L16.25,8.8 + L17.5310733,12.642689 C17.2014325,12.9992627 17,13.4761078 17,14 C17,15.1045695 17.8954305,16 19,16 + C20.1045695,16 21,15.1045695 21,14 C21,13.0901368 20.3924276,12.3221796 19.5608645,12.0797103 Z M21,3 + C22,3 23,4 23,5 L23,19 C23,20 22,21 21,21 L3,21 C1.9,21 1,20.1 1,19 L1,5 C1,4 2,3 3,3 L21,3 Z" /> +</vector> diff --git a/core/res/res/values-mcc310-mnc004/config.xml b/core/res/res/values-mcc310-mnc004/config.xml index 304948893a8d..a328c49cbc71 100755 --- a/core/res/res/values-mcc310-mnc004/config.xml +++ b/core/res/res/values-mcc310-mnc004/config.xml @@ -23,6 +23,7 @@ <string-array translatable="false" name="config_cdma_home_system"> <item>64</item> <item>65</item> + <item>66</item> <item>76</item> <item>77</item> <item>78</item> diff --git a/core/res/res/values-mcc311-mnc480/config.xml b/core/res/res/values-mcc311-mnc480/config.xml index 6f85081bb704..04f182e1a92c 100755 --- a/core/res/res/values-mcc311-mnc480/config.xml +++ b/core/res/res/values-mcc311-mnc480/config.xml @@ -23,6 +23,7 @@ <string-array translatable="false" name="config_cdma_home_system"> <item>64</item> <item>65</item> + <item>66</item> <item>76</item> <item>77</item> <item>78</item> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index e085697628f5..8e7b81740728 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1091,6 +1091,11 @@ that can be set by the user. --> <integer name="config_screenBrightnessDoze">1</integer> + <!-- Whether or not to skip the initial brightness ramps when the display transitions to + STATE_ON. Setting this to true will skip the brightness ramp to the last stored active + brightness value and will repeat for the following ramp if autobrightness is enabled. --> + <bool name="config_skipScreenOnBrightnessRamp">false</bool> + <!-- Allow automatic adjusting of the screen brightness while dozing in low power state. --> <bool name="config_allowAutoBrightnessWhileDozing">false</bool> @@ -2953,4 +2958,10 @@ <!-- The OEM specified sensor string type for the gesture to launch camera app, this value must match the value of config_cameraLiftTriggerSensorType in OEM's HAL --> <string translatable="false" name="config_cameraLiftTriggerSensorStringType"></string> + + <!-- Default number of days to retain for the automatic storage manager. --> + <integer translatable="false" name="config_storageManagerDaystoRetainDefault">90</integer> + + <!-- Name of a font family to use for headlines. If empty, falls back to platform default --> + <string name="config_headlineFontFamily" translatable="false"></string> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 80b72fa46605..5ffa48cb87db 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1762,6 +1762,7 @@ <java-symbol type="bool" name="config_sf_limitedAlpha" /> <java-symbol type="bool" name="config_unplugTurnsOnScreen" /> <java-symbol type="bool" name="config_usbChargingMessage" /> + <java-symbol type="bool" name="config_skipScreenOnBrightnessRamp" /> <java-symbol type="bool" name="config_allowAutoBrightnessWhileDozing" /> <java-symbol type="bool" name="config_allowTheaterModeWakeFromUnplug" /> <java-symbol type="bool" name="config_allowTheaterModeWakeFromGesture" /> @@ -3038,8 +3039,10 @@ <java-symbol type="array" name="config_allowedSecureInstantAppSettings" /> <java-symbol type="bool" name="config_handleVolumeKeysInWindowManager" /> - <java-symbol type="integer" name="config_inCallNotificationVolumeRelative" /> - <java-symbol type="bool" name="config_dozeAlwaysOnDisplayAvailable" /> + <java-symbol type="integer" name="config_storageManagerDaystoRetainDefault" /> + <java-symbol type="string" name="config_headlineFontFamily" /> + + <java-symbol type="drawable" name="stat_sys_vitals" /> </resources> diff --git a/core/tests/coretests/src/android/text/DynamicLayoutTest.java b/core/tests/coretests/src/android/text/DynamicLayoutTest.java index da6dc7edbb5e..811bf2c43320 100644 --- a/core/tests/coretests/src/android/text/DynamicLayoutTest.java +++ b/core/tests/coretests/src/android/text/DynamicLayoutTest.java @@ -17,15 +17,14 @@ package android.text; import static android.text.Layout.Alignment.ALIGN_NORMAL; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import android.graphics.Canvas; +import android.graphics.Paint; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.text.style.ReplacementSpan; @@ -54,6 +53,16 @@ public class DynamicLayoutTest { assertNull(layout.getBlocksAlwaysNeedToBeRedrawn()); } + private class MockReplacementSpan extends ReplacementSpan { + public int getSize(Paint paint, CharSequence text, int start, int end, + Paint.FontMetricsInt fm) { + return 10; + } + + public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, + int y, int bottom, Paint paint) { } + } + @Test public void testGetBlocksAlwaysNeedToBeRedrawn_replacementSpan() { final SpannableStringBuilder builder = new SpannableStringBuilder(); @@ -66,17 +75,11 @@ public class DynamicLayoutTest { builder.append("hijk lmn\n"); assertNull(layout.getBlocksAlwaysNeedToBeRedrawn()); - ReplacementSpan mockReplacementSpan = mock(ReplacementSpan.class); - when(mockReplacementSpan.getSize(any(), any(), any(), any(), any())) - .thenReturn(10); - doNothing().when(mockReplacementSpan) - .draw(any(), any(), any(), any(), any(), any(), any(), any(), any()); - - builder.setSpan(mockReplacementSpan, 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + builder.setSpan(new MockReplacementSpan(), 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); assertNotNull(layout.getBlocksAlwaysNeedToBeRedrawn()); assertTrue(layout.getBlocksAlwaysNeedToBeRedrawn().contains(0)); - builder.setSpan(mockReplacementSpan, 9, 13, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + builder.setSpan(new MockReplacementSpan(), 9, 13, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); assertTrue(layout.getBlocksAlwaysNeedToBeRedrawn().contains(0)); assertTrue(layout.getBlocksAlwaysNeedToBeRedrawn().contains(1)); diff --git a/core/tests/coretests/src/com/android/internal/widget/ActionBarContainerTest.java b/core/tests/coretests/src/com/android/internal/widget/ActionBarContainerTest.java index 1471796553a6..912b7ec02b15 100644 --- a/core/tests/coretests/src/com/android/internal/widget/ActionBarContainerTest.java +++ b/core/tests/coretests/src/com/android/internal/widget/ActionBarContainerTest.java @@ -60,11 +60,9 @@ public class ActionBarContainerTest extends AndroidTestCase { TestViewGroup viewGroup = new TestViewGroup(mContext); viewGroup.addView(mActionBarContainer); - ActionMode mode = mActionBarContainer.startActionModeForChild( - null, null, ActionMode.TYPE_FLOATING); + mActionBarContainer.startActionModeForChild(null, null, ActionMode.TYPE_FLOATING); // Should bubble up. - assertNotNull(mode); assertTrue(viewGroup.isStartActionModeForChildTypedCalled); } diff --git a/drm/java/android/drm/DrmUtils.java b/drm/java/android/drm/DrmUtils.java index 2a86996ef46a..60ee6d94949f 100644 --- a/drm/java/android/drm/DrmUtils.java +++ b/drm/java/android/drm/DrmUtils.java @@ -17,6 +17,7 @@ package android.drm; import java.io.BufferedInputStream; +import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -79,26 +80,16 @@ public class DrmUtils { file.delete(); } - private static void quietlyDispose(InputStream stream) { + private static void quietlyDispose(Closeable closable) { try { - if (null != stream) { - stream.close(); + if (null != closable) { + closable.close(); } } catch (IOException e) { // no need to care, at least as of now } } - private static void quietlyDispose(OutputStream stream) { - try { - if (null != stream) { - stream.close(); - } - } catch (IOException e) { - // no need to care - } - } - /** * Gets an instance of {@link DrmUtils.ExtendedMetadataParser}, which can be used to parse * extended metadata embedded in DRM constraint information. diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 4cfb9d88c716..303d05f084aa 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -141,6 +141,7 @@ cc_defaults { "renderstate/Scissor.cpp", "renderstate/Stencil.cpp", "renderstate/TextureState.cpp", + "renderthread/CacheManager.cpp", "renderthread/CanvasContext.cpp", "renderthread/OpenGLPipeline.cpp", "renderthread/DrawFrameTask.cpp", @@ -300,6 +301,7 @@ cc_test { "tests/unit/BakedOpRendererTests.cpp", "tests/unit/BakedOpStateTests.cpp", "tests/unit/BitmapTests.cpp", + "tests/unit/CacheManagerTests.cpp", "tests/unit/CanvasContextTests.cpp", "tests/unit/CanvasStateTests.cpp", "tests/unit/ClipAreaTests.cpp", diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 88293db193f8..bbbbd5ce51b2 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -50,8 +50,7 @@ TaskManager* SkiaPipeline::getTaskManager() { } void SkiaPipeline::onDestroyHardwareResources() { - // No need to flush the caches here. There is a timer - // which will flush temporary resources over time. + mRenderThread.cacheManager().trimStaleResources(); } bool SkiaPipeline::pinImages(std::vector<SkImage*>& mutableImages) { diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h index 787946f79f6b..4b7a86580621 100644 --- a/libs/hwui/renderstate/RenderState.h +++ b/libs/hwui/renderstate/RenderState.h @@ -45,6 +45,7 @@ class Layer; class DeferredLayerUpdater; namespace renderthread { +class CacheManager; class CanvasContext; class RenderThread; } @@ -55,6 +56,7 @@ class RenderState { PREVENT_COPY_AND_ASSIGN(RenderState); friend class renderthread::RenderThread; friend class Caches; + friend class renderthread::CacheManager; public: void onGLContextCreated(); void onGLContextDestroyed(); diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp new file mode 100644 index 000000000000..f0d6b3860938 --- /dev/null +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2017 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. + */ + +#include "CacheManager.h" + +#include "Layer.h" +#include "RenderThread.h" +#include "renderstate/RenderState.h" + +#include <gui/Surface.h> +#include <GrContextOptions.h> +#include <math.h> +#include <set> + +namespace android { +namespace uirenderer { +namespace renderthread { + +// This multiplier was selected based on historical review of cache sizes relative +// to the screen resolution. This is meant to be a conservative default based on +// that analysis. The 4.0f is used because the default pixel format is assumed to +// be ARGB_8888. +#define SURFACE_SIZE_MULTIPLIER (12.0f * 4.0f) +#define BACKGROUND_RETENTION_PERCENTAGE (0.5f) + +// for super large fonts we will draw them as paths so no need to keep linearly +// increasing the font cache size. +#define FONT_CACHE_MIN_MB (0.5f) +#define FONT_CACHE_MAX_MB (4.0f) + +CacheManager::CacheManager(const DisplayInfo& display) + : mMaxSurfaceArea(display.w * display.h) { + mVectorDrawableAtlas.reset(new VectorDrawableAtlas); +} + +void CacheManager::reset(GrContext* context) { + if (context != mGrContext.get()) { + destroy(); + } + + if (context) { + mGrContext = sk_ref_sp(context); + mGrContext->getResourceCacheLimits(&mMaxResources, nullptr); + updateContextCacheSizes(); + } +} + +void CacheManager::destroy() { + // cleanup any caches here as the GrContext is about to go away... + mGrContext.reset(nullptr); + mVectorDrawableAtlas.reset(new VectorDrawableAtlas); +} + +void CacheManager::updateContextCacheSizes() { + mMaxResourceBytes = mMaxSurfaceArea * SURFACE_SIZE_MULTIPLIER; + mBackgroundResourceBytes = mMaxResourceBytes * BACKGROUND_RETENTION_PERCENTAGE; + + mGrContext->setResourceCacheLimits(mMaxResources, mMaxResourceBytes); +} + +void CacheManager::configureContext(GrContextOptions* contextOptions) { + contextOptions->fAllowPathMaskCaching = true; + + float screenMP = mMaxSurfaceArea / 1024.0f / 1024.0f; + float fontCacheMB = 0; + float decimalVal = std::modf(screenMP, &fontCacheMB); + + // This is a basic heuristic to size the cache to a multiple of 512 KB + if (decimalVal > 0.8f) { + fontCacheMB += 1.0f; + } else if (decimalVal > 0.5f) { + fontCacheMB += 0.5f; + } + + // set limits on min/max size of the cache + fontCacheMB = std::max(FONT_CACHE_MIN_MB, std::min(FONT_CACHE_MAX_MB, fontCacheMB)); + + // We must currently set the size of the text cache based on the size of the + // display even though we like to be dynamicallysizing it to the size of the window. + // Skia's implementation doesn't provide a mechanism to resize the font cache due to + // the potential cost of recreating the glyphs. + contextOptions->fGlyphCacheTextureMaximumBytes = fontCacheMB * 1024 * 1024; +} + +void CacheManager::trimMemory(TrimMemoryMode mode) { + if (!mGrContext) { + return; + } + + mGrContext->flush(); + + switch (mode) { + case TrimMemoryMode::Complete: + mVectorDrawableAtlas.reset(new VectorDrawableAtlas); + mGrContext->freeGpuResources(); + break; + case TrimMemoryMode::UiHidden: + mGrContext->purgeUnlockedResources(mMaxResourceBytes - mBackgroundResourceBytes, true); + break; + } +} + +void CacheManager::trimStaleResources() { + if (!mGrContext) { + return; + } + mGrContext->flush(); + mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(30)); +} + +VectorDrawableAtlas* CacheManager::acquireVectorDrawableAtlas() { + LOG_ALWAYS_FATAL_IF(mVectorDrawableAtlas.get() == nullptr); + LOG_ALWAYS_FATAL_IF(mGrContext == nullptr); + + /** + * TODO LIST: + * 1) compute the atlas based on the surfaceArea surface + * 2) identify a way to reuse cache entries + * 3) add ability to repack the cache? + * 4) define memory conditions where we clear the cache (e.g. surface->reset()) + */ + + return mVectorDrawableAtlas.release(); +} +void CacheManager::releaseVectorDrawableAtlas(VectorDrawableAtlas* atlas) { + LOG_ALWAYS_FATAL_IF(mVectorDrawableAtlas.get() != nullptr); + mVectorDrawableAtlas.reset(atlas); + mVectorDrawableAtlas->isNewAtlas = false; +} + +void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) { + if (!mGrContext) { + log.appendFormat("No valid cache instance.\n"); + return; + } + + size_t bytesCached; + mGrContext->getResourceCacheUsage(nullptr, &bytesCached); + + log.appendFormat("Caches:\n"); + log.appendFormat(" Current / Maximum\n"); + log.appendFormat(" VectorDrawableAtlas %6.2f kB / %6.2f kB (entries = %zu)\n", + 0.0f, 0.0f, (size_t)0); + + if (renderState) { + if (renderState->mActiveLayers.size() > 0) { + log.appendFormat(" Layer Info:\n"); + } + + size_t layerMemoryTotal = 0; + for (std::set<Layer*>::iterator it = renderState->mActiveLayers.begin(); + it != renderState->mActiveLayers.end(); it++) { + const Layer* layer = *it; + const char* layerType = layer->getApi() == Layer::Api::OpenGL ? "GlLayer" : "VkLayer"; + log.appendFormat(" %s size %dx%d\n", layerType, + layer->getWidth(), layer->getHeight()); + layerMemoryTotal += layer->getWidth() * layer->getHeight() * 4; + } + log.appendFormat(" Layers Total %6.2f kB (numLayers = %zu)\n", + layerMemoryTotal / 1024.0f, renderState->mActiveLayers.size()); + } + + + log.appendFormat("Total memory usage:\n"); + log.appendFormat(" %zu bytes, %.2f MB (%.2f MB is purgeable)\n", + bytesCached, bytesCached / 1024.0f / 1024.0f, + mGrContext->getResourceCachePurgeableBytes() / 1024.0f / 1024.0f); + + +} + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h new file mode 100644 index 000000000000..43d58f2d58a8 --- /dev/null +++ b/libs/hwui/renderthread/CacheManager.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2017 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. + */ + +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H + +#include <GrContext.h> +#include <SkSurface.h> +#include <ui/DisplayInfo.h> +#include <utils/String8.h> +#include <vector> + +namespace android { + +class Surface; + +namespace uirenderer { + +class RenderState; + +namespace renderthread { + +class IRenderPipeline; +class RenderThread; + +struct VectorDrawableAtlas { + sk_sp<SkSurface> surface; + bool isNewAtlas = true; +}; + +class CacheManager { +public: + enum class TrimMemoryMode { + Complete, + UiHidden + }; + + void configureContext(GrContextOptions* context); + void trimMemory(TrimMemoryMode mode); + void trimStaleResources(); + void dumpMemoryUsage(String8& log, const RenderState* renderState = nullptr); + + VectorDrawableAtlas* acquireVectorDrawableAtlas(); + void releaseVectorDrawableAtlas(VectorDrawableAtlas*); + + size_t getCacheSize() const { return mMaxResourceBytes; } + size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; } + +private: + friend class RenderThread; + + CacheManager(const DisplayInfo& display); + + + void reset(GrContext* grContext); + void destroy(); + void updateContextCacheSizes(); + + const size_t mMaxSurfaceArea; + sk_sp<GrContext> mGrContext; + + int mMaxResources = 0; + size_t mMaxResourceBytes = 0; + size_t mBackgroundResourceBytes = 0; + + struct PipelineProps { + const void* pipelineKey = nullptr; + size_t surfaceArea = 0; + }; + + std::unique_ptr<VectorDrawableAtlas> mVectorDrawableAtlas; +}; + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* CACHEMANAGER_H */ + diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index e2a4a2a8dec2..a79bf359913b 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -585,15 +585,37 @@ void CanvasContext::destroyHardwareResources() { } void CanvasContext::trimMemory(RenderThread& thread, int level) { - // No context means nothing to free - if (!thread.eglManager().hasEglContext()) return; - - ATRACE_CALL(); - if (level >= TRIM_MEMORY_COMPLETE) { - thread.renderState().flush(Caches::FlushMode::Full); - thread.eglManager().destroy(); - } else if (level >= TRIM_MEMORY_UI_HIDDEN) { - thread.renderState().flush(Caches::FlushMode::Moderate); + auto renderType = Properties::getRenderPipelineType(); + switch (renderType) { + case RenderPipelineType::OpenGL: { + // No context means nothing to free + if (!thread.eglManager().hasEglContext()) return; + ATRACE_CALL(); + if (level >= TRIM_MEMORY_COMPLETE) { + thread.renderState().flush(Caches::FlushMode::Full); + thread.eglManager().destroy(); + } else if (level >= TRIM_MEMORY_UI_HIDDEN) { + thread.renderState().flush(Caches::FlushMode::Moderate); + } + break; + } + case RenderPipelineType::SkiaGL: + case RenderPipelineType::SkiaVulkan: { + // No context means nothing to free + if (!thread.getGrContext()) return; + ATRACE_CALL(); + if (level >= TRIM_MEMORY_COMPLETE) { + thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete); + thread.eglManager().destroy(); + thread.vulkanManager().destroy(); + } else if (level >= TRIM_MEMORY_UI_HIDDEN) { + thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden); + } + break; + } + default: + LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType); + break; } } diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 53d42a2b9c43..ecf686c5b40c 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -138,7 +138,7 @@ void EglManager::initialize() { GrContextOptions options; options.fGpuPathRenderers &= ~GrContextOptions::GpuPathRenderers::kDistanceField; - options.fAllowPathMaskCaching = true; + mRenderThread.cacheManager().configureContext(&options); mRenderThread.setGrContext(GrContext::Create(GrBackend::kOpenGL_GrBackend, (GrBackendContext)glInterface.get(), options)); } diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 5a4695f1315e..ec56c313f62a 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -469,18 +469,7 @@ uint32_t RenderProxy::frameTimePercentile(int p) { } CREATE_BRIDGE2(dumpGraphicsMemory, int fd, RenderThread* thread) { - args->thread->jankTracker().dump(args->fd); - - FILE *file = fdopen(args->fd, "a"); - if (Caches::hasInstance()) { - String8 cachesLog; - Caches::getInstance().dumpMemoryUsage(cachesLog); - fprintf(file, "\nCaches:\n%s\n", cachesLog.string()); - } else { - fprintf(file, "\nNo caches instance.\n"); - } - fprintf(file, "\nPipeline=FrameBuilder\n"); - fflush(file); + args->thread->dumpGraphicsMemory(args->fd); return nullptr; } diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 055458397023..13af2c4d15e8 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -202,6 +202,45 @@ void RenderThread::initThreadLocals() { mRenderState = new RenderState(*this); mJankTracker = new JankTracker(mDisplayInfo); mVkManager = new VulkanManager(*this); + mCacheManager = new CacheManager(mDisplayInfo); +} + +void RenderThread::dumpGraphicsMemory(int fd) { + jankTracker().dump(fd); + + String8 cachesOutput; + String8 pipeline; + auto renderType = Properties::getRenderPipelineType(); + switch (renderType) { + case RenderPipelineType::OpenGL: { + if (Caches::hasInstance()) { + cachesOutput.appendFormat("Caches:\n"); + Caches::getInstance().dumpMemoryUsage(cachesOutput); + } else { + cachesOutput.appendFormat("No caches instance."); + } + pipeline.appendFormat("FrameBuilder"); + break; + } + case RenderPipelineType::SkiaGL: { + mCacheManager->dumpMemoryUsage(cachesOutput, mRenderState); + pipeline.appendFormat("Skia (OpenGL)"); + break; + } + case RenderPipelineType::SkiaVulkan: { + mCacheManager->dumpMemoryUsage(cachesOutput, mRenderState); + pipeline.appendFormat("Skia (Vulkan)"); + break; + } + default: + LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType); + break; + } + + FILE *file = fdopen(fd, "a"); + fprintf(file, "\n%s\n", cachesOutput.string()); + fprintf(file, "\nPipeline=%s\n", pipeline.string()); + fflush(file); } Readback& RenderThread::readback() { @@ -228,6 +267,14 @@ Readback& RenderThread::readback() { return *mReadback; } +void RenderThread::setGrContext(GrContext* context) { + mCacheManager->reset(context); + if (mGrContext.get()) { + mGrContext->releaseResourcesAndAbandonContext(); + } + mGrContext.reset(context); +} + int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) { if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { ALOGE("Display event receiver pipe was closed or an error occurred. " diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index 4b5601c5abc4..d9842572d7cd 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -20,6 +20,7 @@ #include "RenderTask.h" #include "../JankTracker.h" +#include "CacheManager.h" #include "TimeLord.h" #include <GrContext.h> @@ -102,11 +103,13 @@ public: const DisplayInfo& mainDisplayInfo() { return mDisplayInfo; } GrContext* getGrContext() const { return mGrContext.get(); } - void setGrContext(GrContext* cxt) { mGrContext.reset(cxt); } + void setGrContext(GrContext* cxt); + CacheManager& cacheManager() { return *mCacheManager; } VulkanManager& vulkanManager() { return *mVkManager; } sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& skBitmap); + void dumpGraphicsMemory(int fd); protected: virtual bool threadLoop() override; @@ -161,6 +164,7 @@ private: Readback* mReadback = nullptr; sk_sp<GrContext> mGrContext; + CacheManager* mCacheManager; VulkanManager* mVkManager; }; diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp new file mode 100644 index 000000000000..6115162c8f81 --- /dev/null +++ b/libs/hwui/tests/unit/CacheManagerTests.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2017 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. + */ + +#include <gtest/gtest.h> + +#include "renderthread/CacheManager.h" +#include "renderthread/EglManager.h" +#include "tests/common/TestUtils.h" + +using namespace android; +using namespace android::uirenderer; +using namespace android::uirenderer::renderthread; + +static size_t getCacheUsage(GrContext* grContext) { + size_t cacheUsage; + grContext->getResourceCacheUsage(nullptr, &cacheUsage); + return cacheUsage; +} + +RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) { + DisplayInfo displayInfo = renderThread.mainDisplayInfo(); + GrContext* grContext = renderThread.getGrContext(); + ASSERT_TRUE(grContext != nullptr); + + // create pairs of offscreen render targets and images until we exceed the backgroundCacheSizeLimit + std::vector<sk_sp<SkSurface>> surfaces; + + while (getCacheUsage(grContext) <= renderThread.cacheManager().getBackgroundCacheSize()) { + SkImageInfo info = SkImageInfo::MakeA8(displayInfo.w, displayInfo.h); + sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(grContext, SkBudgeted::kYes, info); + surface->getCanvas()->drawColor(SK_AlphaTRANSPARENT); + + grContext->flush(); + + surfaces.push_back(surface); + } + + ASSERT_TRUE(1 < surfaces.size()); + + // attempt to trim all memory while we still hold strong refs + renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete); + ASSERT_TRUE(0 == grContext->getResourceCachePurgeableBytes()); + + // free the surfaces + for (size_t i = 0; i < surfaces.size(); i++) { + ASSERT_TRUE(surfaces[i]->unique()); + surfaces[i].reset(); + } + + // verify that we have enough purgeable bytes + const size_t purgeableBytes = grContext->getResourceCachePurgeableBytes(); + ASSERT_TRUE(renderThread.cacheManager().getBackgroundCacheSize() < purgeableBytes); + + // UI hidden and make sure only some got purged + renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden); + ASSERT_TRUE(0 < grContext->getResourceCachePurgeableBytes()); + ASSERT_TRUE(renderThread.cacheManager().getBackgroundCacheSize() > getCacheUsage(grContext)); + + // complete and make sure all get purged + renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete); + ASSERT_TRUE(0 == grContext->getResourceCachePurgeableBytes()); +} diff --git a/location/java/android/location/CountryDetector.java b/location/java/android/location/CountryDetector.java index ce3c56f73c01..ec6dfb713b10 100644 --- a/location/java/android/location/CountryDetector.java +++ b/location/java/android/location/CountryDetector.java @@ -18,6 +18,8 @@ package android.location; import java.util.HashMap; +import android.annotation.SystemService; +import android.content.Context; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; @@ -40,13 +42,10 @@ import android.util.Log; * To be notified of the future country change, use the * {@link #addCountryListener} * <p> - * <p> - * You do not instantiate this class directly; instead, retrieve it through - * {@link android.content.Context#getSystemService - * Context.getSystemService(Context.COUNTRY_DETECTOR)}. * * @hide */ +@SystemService(Context.COUNTRY_DETECTOR) public class CountryDetector { /** diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index f9385c6d3b7f..26ac2a23d8c5 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -20,7 +20,9 @@ import com.android.internal.location.ProviderProperties; import android.Manifest; import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.annotation.TestApi; import android.app.PendingIntent; import android.content.Context; @@ -47,11 +49,6 @@ import static android.Manifest.permission.ACCESS_FINE_LOCATION; * {@link Intent} when the device enters the proximity of a given * geographical location. * - * <p>You do not - * instantiate this class directly; instead, retrieve it through - * {@link android.content.Context#getSystemService - * Context.getSystemService(Context.LOCATION_SERVICE)}. - * * <p class="note">Unless noted, all Location API methods require * the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} or * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permissions. @@ -60,8 +57,8 @@ import static android.Manifest.permission.ACCESS_FINE_LOCATION; * return location results, but the update rate will be throttled and the exact * location will be obfuscated to a coarse level of accuracy. */ -public class LocationManager -{ +@SystemService(Context.LOCATION_SERVICE) +public class LocationManager { private static final String TAG = "LocationManager"; private final Context mContext; @@ -831,6 +828,7 @@ public class LocationManager * @hide */ @SystemApi + @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) public void requestLocationUpdates(LocationRequest request, LocationListener listener, Looper looper) { checkListener(listener); @@ -859,6 +857,7 @@ public class LocationManager * @hide */ @SystemApi + @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) public void requestLocationUpdates(LocationRequest request, PendingIntent intent) { checkPendingIntent(intent); requestLocationUpdates(request, null, null, intent); @@ -1820,6 +1819,7 @@ public class LocationManager */ @Deprecated @SystemApi + @SuppressLint("Doclava125") public boolean addGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) { return false; } @@ -1857,6 +1857,7 @@ public class LocationManager */ @Deprecated @SystemApi + @SuppressLint("Doclava125") public void removeGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) { } @@ -1877,6 +1878,7 @@ public class LocationManager */ @Deprecated @SystemApi + @SuppressLint("Doclava125") public boolean addGpsNavigationMessageListener(GpsNavigationMessageEvent.Listener listener) { return false; } @@ -1891,6 +1893,7 @@ public class LocationManager */ @Deprecated @SystemApi + @SuppressLint("Doclava125") public void removeGpsNavigationMessageListener(GpsNavigationMessageEvent.Listener listener) { } diff --git a/lowpan/Android.mk b/lowpan/Android.mk new file mode 100644 index 000000000000..9e9164f51513 --- /dev/null +++ b/lowpan/Android.mk @@ -0,0 +1,31 @@ +# +# Copyright (C) 2017 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. +# + +LOCAL_PATH := $(call my-dir) + +ifneq (,$(findstring lowpan/java,$(FRAMEWORKS_BASE_SUBDIRS))) +include $(CLEAR_VARS) +LOCAL_MODULE := libandroid_net_lowpan +LOCAL_MODULE_TAGS := optional +LOCAL_SHARED_LIBRARIES += libbase +LOCAL_SHARED_LIBRARIES += libbinder +LOCAL_SHARED_LIBRARIES += libutils +LOCAL_AIDL_INCLUDES += frameworks/native/aidl/binder +LOCAL_AIDL_INCLUDES += frameworks/base/lowpan/java +LOCAL_AIDL_INCLUDES += frameworks/base/core/java +LOCAL_SRC_FILES += $(call all-Iaidl-files-under, java/android/net/lowpan) +include $(BUILD_SHARED_LIBRARY) +endif diff --git a/lowpan/java/android/net/lowpan/ILowpanEnergyScanCallback.aidl b/lowpan/java/android/net/lowpan/ILowpanEnergyScanCallback.aidl new file mode 100644 index 000000000000..f09dbe3d077e --- /dev/null +++ b/lowpan/java/android/net/lowpan/ILowpanEnergyScanCallback.aidl @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2017 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.net.lowpan; + +/** {@hide} */ +interface ILowpanEnergyScanCallback { + oneway void onEnergyScanResult(int channel, int rssi); + oneway void onEnergyScanFinished(); +} diff --git a/lowpan/java/android/net/lowpan/ILowpanInterface.aidl b/lowpan/java/android/net/lowpan/ILowpanInterface.aidl new file mode 100644 index 000000000000..647fcc1eef3d --- /dev/null +++ b/lowpan/java/android/net/lowpan/ILowpanInterface.aidl @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2017 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.net.lowpan; + +import android.net.lowpan.ILowpanInterfaceListener; +import android.net.lowpan.ILowpanNetScanCallback; +import android.net.lowpan.ILowpanEnergyScanCallback; +import android.os.PersistableBundle; +import android.net.IpPrefix; + +/** {@hide} */ +interface ILowpanInterface { + + ////////////////////////////////////////////////////////////////////////// + // Permission String Constants + + /* These are here for the sake of C++ interface implementations. */ + + const String PERM_ACCESS_LOWPAN_STATE = "android.permission.ACCESS_LOWPAN_STATE"; + const String PERM_CHANGE_LOWPAN_STATE = "android.permission.CHANGE_LOWPAN_STATE"; + const String PERM_READ_LOWPAN_CREDENTIAL = "android.permission.READ_LOWPAN_CREDENTIAL"; + + ////////////////////////////////////////////////////////////////////////// + // Property Key Constants + + const String KEY_INTERFACE_ENABLED = "android.net.lowpan.property.INTERFACE_ENABLED"; + const String KEY_INTERFACE_UP = "android.net.lowpan.property.INTERFACE_UP"; + const String KEY_INTERFACE_COMMISSIONED = "android.net.lowpan.property.INTERFACE_COMMISSIONED"; + const String KEY_INTERFACE_CONNECTED = "android.net.lowpan.property.INTERFACE_CONNECTED"; + const String KEY_INTERFACE_STATE = "android.net.lowpan.property.INTERFACE_STATE"; + + const String KEY_NETWORK_NAME = "android.net.lowpan.property.NETWORK_NAME"; + const String KEY_NETWORK_TYPE = "android.net.lowpan.property.NETWORK_TYPE"; + const String KEY_NETWORK_PANID = "android.net.lowpan.property.NETWORK_PANID"; + const String KEY_NETWORK_XPANID = "android.net.lowpan.property.NETWORK_XPANID"; + const String KEY_NETWORK_ROLE = "android.net.lowpan.property.NETWORK_ROLE"; + const String KEY_NETWORK_MASTER_KEY = "android.net.lowpan.property.NETWORK_MASTER_KEY"; + const String KEY_NETWORK_MASTER_KEY_INDEX + = "android.net.lowpan.property.NETWORK_MASTER_KEY_INDEX"; + + const String KEY_SUPPORTED_CHANNELS = "android.net.lowpan.property.SUPPORTED_CHANNELS"; + const String KEY_CHANNEL = "android.net.lowpan.property.CHANNEL"; + const String KEY_CHANNEL_MASK = "android.net.lowpan.property.CHANNEL_MASK"; + const String KEY_MAX_TX_POWER = "android.net.lowpan.property.MAX_TX_POWER"; + const String KEY_RSSI = "android.net.lowpan.property.RSSI"; + const String KEY_LQI = "android.net.lowpan.property.LQI"; + + const String KEY_LINK_ADDRESS_ARRAY = "android.net.lowpan.property.LINK_ADDRESS_ARRAY"; + const String KEY_ROUTE_INFO_ARRAY = "android.net.lowpan.property.ROUTE_INFO_ARRAY"; + + const String KEY_BEACON_ADDRESS = "android.net.lowpan.property.BEACON_ORIGIN_ADDRESS"; + const String KEY_BEACON_CAN_ASSIST = "android.net.lowpan.property.BEACON_CAN_ASSIST"; + + const String DRIVER_VERSION = "android.net.lowpan.property.DRIVER_VERSION"; + const String NCP_VERSION = "android.net.lowpan.property.NCP_VERSION"; + + /** @hide */ + const String KEY_EXTENDED_ADDRESS = "android.net.lowpan.property.EXTENDED_ADDRESS"; + + /** @hide */ + const String KEY_MAC_ADDRESS = "android.net.lowpan.property.MAC_ADDRESS"; + + ////////////////////////////////////////////////////////////////////////// + // Interface States + + const String STATE_OFFLINE = "offline"; + const String STATE_COMMISSIONING = "commissioning"; + const String STATE_ATTACHING = "attaching"; + const String STATE_ATTACHED = "attached"; + const String STATE_FAULT = "fault"; + + ////////////////////////////////////////////////////////////////////////// + // Device Roles + + const String ROLE_END_DEVICE = "end-device"; + const String ROLE_ROUTER = "router"; + const String ROLE_SLEEPY_END_DEVICE = "sleepy-end-device"; + const String ROLE_SLEEPY_ROUTER = "sleepy-router"; + const String ROLE_UNKNOWN = "unknown"; + + ////////////////////////////////////////////////////////////////////////// + // Service-Specific Error Code Constants + + const int ERROR_UNSPECIFIED = 1; + const int ERROR_INVALID_ARGUMENT = 2; + const int ERROR_DISABLED = 3; + const int ERROR_WRONG_STATE = 4; + const int ERROR_INVALID_TYPE = 5; + const int ERROR_INVALID_VALUE = 6; + const int ERROR_TIMEOUT = 7; + const int ERROR_IO_FAILURE = 8; + const int ERROR_BUSY = 9; + const int ERROR_ALREADY = 10; + const int ERROR_CANCELED = 11; + const int ERROR_CREDENTIAL_NEEDED = 12; + const int ERROR_FEATURE_NOT_SUPPORTED = 14; + const int ERROR_PROPERTY_NOT_FOUND = 16; + const int ERROR_JOIN_FAILED_UNKNOWN = 18; + const int ERROR_JOIN_FAILED_AT_SCAN = 19; + const int ERROR_JOIN_FAILED_AT_AUTH = 20; + const int ERROR_FORM_FAILED_AT_SCAN = 21; + const int ERROR_NCP_PROBLEM = 27; + const int ERROR_PERMISSION_DENIED = 28; + + ////////////////////////////////////////////////////////////////////////// + // Methods + + @utf8InCpp String getName(); + + void join(in Map parameters); + void form(in Map parameters); + void leave(); + void reset(); + + void beginLowPower(); + void pollForData(); + + oneway void onHostWake(); + + @utf8InCpp String[] getPropertyKeys(); + Map getProperties(in @utf8InCpp String[] keys); + void setProperties(in Map properties); + + void addListener(ILowpanInterfaceListener listener); + oneway void removeListener(ILowpanInterfaceListener listener); + + void startNetScan(in Map properties, ILowpanNetScanCallback listener); + oneway void stopNetScan(); + + void startEnergyScan(in Map properties, ILowpanEnergyScanCallback listener); + oneway void stopEnergyScan(); + + void addOnMeshPrefix(in IpPrefix prefix, int flags); + oneway void removeOnMeshPrefix(in IpPrefix prefix); + + void addExternalRoute(in IpPrefix prefix, int flags); + oneway void removeExternalRoute(in IpPrefix prefix); + + @utf8InCpp String getPropertyAsString(@utf8InCpp String key); +} diff --git a/lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl b/lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl new file mode 100644 index 000000000000..c99d732d4eba --- /dev/null +++ b/lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2017 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.net.lowpan; + +/** {@hide} */ +interface ILowpanInterfaceListener { + oneway void onPropertiesChanged(in Map properties); +} diff --git a/lowpan/java/android/net/lowpan/ILowpanManager.aidl b/lowpan/java/android/net/lowpan/ILowpanManager.aidl new file mode 100644 index 000000000000..5a8d7dce7c6f --- /dev/null +++ b/lowpan/java/android/net/lowpan/ILowpanManager.aidl @@ -0,0 +1,35 @@ +/* + * 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.net.lowpan; +import android.net.lowpan.ILowpanInterface; +import android.net.lowpan.ILowpanManagerListener; + +/** {@hide} */ +interface ILowpanManager { + + const String LOWPAN_SERVICE_NAME = "lowpan"; + + ILowpanInterface getInterface(@utf8InCpp String name); + + @utf8InCpp String[] getInterfaceList(); + + void addListener(ILowpanManagerListener listener); + void removeListener(ILowpanManagerListener listener); + + void addInterface(ILowpanInterface lowpan_interface); + void removeInterface(ILowpanInterface lowpan_interface); +} diff --git a/lowpan/java/android/net/lowpan/ILowpanManagerListener.aidl b/lowpan/java/android/net/lowpan/ILowpanManagerListener.aidl new file mode 100644 index 000000000000..d4846f6740b9 --- /dev/null +++ b/lowpan/java/android/net/lowpan/ILowpanManagerListener.aidl @@ -0,0 +1,25 @@ +/* + * 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.net.lowpan; + +import android.net.lowpan.ILowpanInterface; + +/** {@hide} */ +interface ILowpanManagerListener { + oneway void onInterfaceAdded(ILowpanInterface lowpanInterface); + oneway void onInterfaceRemoved(ILowpanInterface lowpanInterface); +} diff --git a/lowpan/java/android/net/lowpan/ILowpanNetScanCallback.aidl b/lowpan/java/android/net/lowpan/ILowpanNetScanCallback.aidl new file mode 100644 index 000000000000..c20a6f8110c8 --- /dev/null +++ b/lowpan/java/android/net/lowpan/ILowpanNetScanCallback.aidl @@ -0,0 +1,23 @@ +/* + * 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.net.lowpan; + +/** {@hide} */ +interface ILowpanNetScanCallback { + oneway void onNetScanBeacon(in Map parameters); + oneway void onNetScanFinished(); +} diff --git a/lowpan/java/android/net/lowpan/LowpanBeaconInfo.java b/lowpan/java/android/net/lowpan/LowpanBeaconInfo.java new file mode 100644 index 000000000000..b344527e4be5 --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanBeaconInfo.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2017 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.net.lowpan; + +import com.android.internal.util.HexDump; +import java.util.Collection; +import java.util.Map; +import java.util.TreeSet; + +/** + * Describes a LoWPAN Beacon + * + * @hide + */ +//@SystemApi +public class LowpanBeaconInfo extends LowpanIdentity { + + private int mRssi = UNKNOWN; + private int mLqi = UNKNOWN; + private byte[] mBeaconAddress = null; + private final TreeSet<Integer> mFlags = new TreeSet<>(); + + public static final int FLAG_CAN_ASSIST = 1; + + static class Builder extends LowpanIdentity.Builder { + private final LowpanBeaconInfo identity = new LowpanBeaconInfo(); + + public Builder setRssi(int x) { + identity.mRssi = x; + return this; + } + + public Builder setLqi(int x) { + identity.mLqi = x; + return this; + } + + public Builder setBeaconAddress(byte x[]) { + identity.mBeaconAddress = x.clone(); + return this; + } + + public Builder setFlag(int x) { + identity.mFlags.add(x); + return this; + } + + public Builder setFlags(Collection<Integer> x) { + identity.mFlags.addAll(x); + return this; + } + + /** @hide */ + Builder updateFromMap(Map map) { + if (map.containsKey(LowpanProperties.KEY_RSSI.getName())) { + setRssi(LowpanProperties.KEY_RSSI.getFromMap(map)); + } + if (map.containsKey(LowpanProperties.KEY_LQI.getName())) { + setLqi(LowpanProperties.KEY_LQI.getFromMap(map)); + } + if (map.containsKey(LowpanProperties.KEY_BEACON_ADDRESS.getName())) { + setBeaconAddress(LowpanProperties.KEY_BEACON_ADDRESS.getFromMap(map)); + } + identity.mFlags.clear(); + if (map.containsKey(LowpanProperties.KEY_BEACON_CAN_ASSIST.getName()) + && LowpanProperties.KEY_BEACON_CAN_ASSIST.getFromMap(map).booleanValue()) { + setFlag(FLAG_CAN_ASSIST); + } + super.updateFromMap(map); + return this; + } + + public LowpanBeaconInfo build() { + return identity; + } + } + + private LowpanBeaconInfo() {} + + public int getRssi() { + return mRssi; + } + + public int getLqi() { + return mLqi; + } + + public byte[] getBeaconAddress() { + return mBeaconAddress.clone(); + } + + public Collection<Integer> getFlags() { + return mFlags.clone(); + } + + public boolean isFlagSet(int flag) { + return mFlags.contains(flag); + } + + @Override + void addToMap(Map<String, Object> parameters) { + super.addToMap(parameters); + } + + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + + sb.append(super.toString()); + + if (mRssi != UNKNOWN) { + sb.append(", RSSI: ").append(mRssi); + } + + if (mLqi != UNKNOWN) { + sb.append(", LQI: ").append(mLqi); + } + + if (mBeaconAddress != null) { + sb.append(", BeaconAddress: ").append(HexDump.toHexString(mBeaconAddress)); + } + + for (Integer flag : mFlags) { + switch (flag.intValue()) { + case FLAG_CAN_ASSIST: + sb.append(", CAN_ASSIST"); + break; + default: + sb.append(", FLAG_").append(Integer.toHexString(flag)); + break; + } + } + + return sb.toString(); + } +} diff --git a/lowpan/java/android/net/lowpan/LowpanChannelInfo.java b/lowpan/java/android/net/lowpan/LowpanChannelInfo.java new file mode 100644 index 000000000000..50afe6d3a4c0 --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanChannelInfo.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2017 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.net.lowpan; + + +/** Provides detailed information about a given channel. */ +//@SystemApi +public class LowpanChannelInfo { + + public static final int UNKNOWN_POWER = Integer.MAX_VALUE; + + ////////////////////////////////////////////////////////////////////////// + // Instance Variables + + private String mName = null; + private int mIndex = 0; + private boolean mIsMaskedByRegulatoryDomain = false; + private float mSpectrumCenterFrequency = 0.0f; + private float mSpectrumBandwidth = 0.0f; + private int mMaxTransmitPower = UNKNOWN_POWER; + + ////////////////////////////////////////////////////////////////////////// + // Public Getters and Setters + + public String getName() { + return mName; + } + + public int getIndex() { + return mIndex; + } + + public int getMaxTransmitPower() { + return mMaxTransmitPower; + } + + public boolean isMaskedByRegulatoryDomain() { + return mIsMaskedByRegulatoryDomain; + } + + public float getSpectrumCenterFrequency() { + return mSpectrumCenterFrequency; + } + + public float getSpectrumBandwidth() { + return mSpectrumBandwidth; + } + + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + + sb.append("Channel ").append(mIndex); + + if (mName != null) { + sb.append(" (").append(mName).append(")"); + } + + if (mSpectrumCenterFrequency > 0.0f) { + sb.append(", SpectrumCenterFrequency: ").append(mSpectrumCenterFrequency).append("Hz"); + } + + if (mSpectrumBandwidth > 0.0f) { + sb.append(", SpectrumBandwidth: ").append(mSpectrumBandwidth).append("Hz"); + } + + if (mMaxTransmitPower != UNKNOWN_POWER) { + sb.append(", MaxTransmitPower: ").append(mMaxTransmitPower); + } + + return sb.toString(); + } +} diff --git a/lowpan/java/android/net/lowpan/LowpanCommissioningSession.java b/lowpan/java/android/net/lowpan/LowpanCommissioningSession.java new file mode 100644 index 000000000000..9cad00c3415a --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanCommissioningSession.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2017 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.net.lowpan; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.Network; +import android.os.Handler; +import java.net.InetSocketAddress; + +/** + * Commissioning Session. + * + * <p>This class enables a device to learn the credential needed to join a network using a technique + * called "in-band commissioning". + * + * @hide + */ +//@SystemApi +public abstract class LowpanCommissioningSession { + public LowpanCommissioningSession() {} + + /** + * Callback base class for {@link LowpanCommissioningSession} + * + * @hide + */ + //@SystemApi + public class Callback { + public void onReceiveFromCommissioner(@NonNull byte[] packet) {}; + + public void onClosed() {}; + } + + /** TODO: doc */ + @NonNull + public abstract LowpanBeaconInfo getBeaconInfo(); + + /** TODO: doc */ + public abstract void sendToCommissioner(@NonNull byte[] packet); + + /** TODO: doc */ + public abstract void setCallback(@Nullable Callback cb, @Nullable Handler handler); + + /** TODO: doc */ + public abstract void close(); + + /** + * This method is largely for Nest Weave, as an alternative to {@link #sendToCommissioner()} + * and @{link Callback#onReceiveFromCommissioner()}. + * + * <p>When used with the Network instance obtained from getNetwork(), the caller can use the + * given InetSocketAddress to communicate with the commissioner using a UDP (or, under certain + * circumstances, TCP) socket. + */ + public abstract @Nullable InetSocketAddress getInetSocketAddress(); + + public abstract @Nullable Network getNetwork(); +} diff --git a/lowpan/java/android/net/lowpan/LowpanCredential.java b/lowpan/java/android/net/lowpan/LowpanCredential.java new file mode 100644 index 000000000000..dea4d7888884 --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanCredential.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2017 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.net.lowpan; + + +import java.util.Map; + +/** + * Describes a credential for a LoWPAN network. + * + * @hide + */ +//@SystemApi +public class LowpanCredential { + + public static final int UNSPECIFIED_KEY_INDEX = 0; + + private byte[] mMasterKey = null; + private int mMasterKeyIndex = UNSPECIFIED_KEY_INDEX; + + LowpanCredential() {} + + private LowpanCredential(byte[] masterKey, int keyIndex) { + setMasterKey(masterKey, keyIndex); + } + + private LowpanCredential(byte[] masterKey) { + setMasterKey(masterKey); + } + + public static LowpanCredential createMasterKey(byte[] masterKey) { + return new LowpanCredential(masterKey); + } + + public static LowpanCredential createMasterKey(byte[] masterKey, int keyIndex) { + return new LowpanCredential(masterKey, keyIndex); + } + + public void setMasterKey(byte[] masterKey) { + if (masterKey != null) { + masterKey = masterKey.clone(); + } + mMasterKey = masterKey; + } + + public void setMasterKeyIndex(int keyIndex) { + mMasterKeyIndex = keyIndex; + } + + public void setMasterKey(byte[] masterKey, int keyIndex) { + setMasterKey(masterKey); + setMasterKeyIndex(keyIndex); + } + + public byte[] getMasterKey() { + if (mMasterKey != null) { + return mMasterKey.clone(); + } + return null; + } + + public int getMasterKeyIndex() { + return mMasterKeyIndex; + } + + public boolean isMasterKey() { + return mMasterKey != null; + } + + void addToMap(Map<String, Object> parameters) throws LowpanException { + if (isMasterKey()) { + LowpanProperties.KEY_NETWORK_MASTER_KEY.putInMap(parameters, getMasterKey()); + LowpanProperties.KEY_NETWORK_MASTER_KEY_INDEX.putInMap( + parameters, getMasterKeyIndex()); + } else { + throw new LowpanException("Unsupported Network Credential"); + } + } +} diff --git a/lowpan/java/android/net/lowpan/LowpanEnergyScanResult.java b/lowpan/java/android/net/lowpan/LowpanEnergyScanResult.java new file mode 100644 index 000000000000..c680687d0e09 --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanEnergyScanResult.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2017 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.net.lowpan; + + +/** + * Describes the result from one channel of an energy scan. + * + * @hide + */ +//@SystemApi +public class LowpanEnergyScanResult { + public static final int UNKNOWN = Integer.MAX_VALUE; + + private int mChannel = UNKNOWN; + private int mMaxRssi = UNKNOWN; + + public LowpanEnergyScanResult() {} + + public int getChannel() { + return mChannel; + } + + public int getMaxRssi() { + return mMaxRssi; + } + + public void setChannel(int x) { + mChannel = x; + } + + public void setMaxRssi(int x) { + mMaxRssi = x; + } + + @Override + public String toString() { + return "LowpanEnergyScanResult(channel: " + mChannel + ", maxRssi:" + mMaxRssi + ")"; + } +} diff --git a/lowpan/java/android/net/lowpan/LowpanException.java b/lowpan/java/android/net/lowpan/LowpanException.java new file mode 100644 index 000000000000..8ff37f926899 --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanException.java @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2017 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.net.lowpan; + +import android.os.DeadObjectException; +import android.os.RemoteException; +import android.os.ServiceSpecificException; +import android.util.AndroidException; + +/** + * <code>LowpanException</code> is thrown if an action to a LoWPAN interface could not be performed + * or a LoWPAN interface property could not be fetched or changed. + * + * @see LowpanInterface + * @hide + */ +//@SystemApi +public class LowpanException extends AndroidException { + // Make the eclipse warning about serializable exceptions go away + private static final long serialVersionUID = 0x31863cbe562b0e11l; // randomly generated + + public static final int LOWPAN_ERROR = 1; + public static final int LOWPAN_CREDENTIAL_NEEDED = 2; + public static final int LOWPAN_DEAD = 3; + public static final int LOWPAN_DISABLED = 4; + public static final int LOWPAN_WRONG_STATE = 5; + public static final int LOWPAN_BUSY = 7; + public static final int LOWPAN_NCP_PROBLEM = 8; + public static final int LOWPAN_ALREADY = 9; + public static final int LOWPAN_CANCELED = 10; + public static final int LOWPAN_FEATURE_NOT_SUPPORTED = 12; + public static final int LOWPAN_PROPERTY_NOT_FOUND = 13; + public static final int LOWPAN_JOIN_FAILED_UNKNOWN = 14; + public static final int LOWPAN_JOIN_FAILED_AT_SCAN = 15; + public static final int LOWPAN_JOIN_FAILED_AT_AUTH = 16; + public static final int LOWPAN_FORM_FAILED_AT_SCAN = 17; + + /** + * Convert ServiceSpecificExceptions and Binder RemoteExceptions from LoWPAN binder interfaces + * into the correct public exceptions. + * + * @hide + */ + public static void throwAsPublicException(Throwable t) throws LowpanException { + if (t instanceof ServiceSpecificException) { + ServiceSpecificException e = (ServiceSpecificException) t; + int reason; + switch (e.errorCode) { + case ILowpanInterface.ERROR_INVALID_ARGUMENT: + case ILowpanInterface.ERROR_INVALID_TYPE: + case ILowpanInterface.ERROR_INVALID_VALUE: + throw new IllegalArgumentException(e.getMessage(), e); + + case ILowpanInterface.ERROR_PERMISSION_DENIED: + throw new SecurityException(e.getMessage(), e); + + case ILowpanInterface.ERROR_DISABLED: + reason = LowpanException.LOWPAN_DISABLED; + break; + + case ILowpanInterface.ERROR_WRONG_STATE: + reason = LowpanException.LOWPAN_WRONG_STATE; + break; + + case ILowpanInterface.ERROR_BUSY: + reason = LowpanException.LOWPAN_BUSY; + break; + + case ILowpanInterface.ERROR_ALREADY: + reason = LowpanException.LOWPAN_ALREADY; + break; + + case ILowpanInterface.ERROR_CANCELED: + reason = LowpanException.LOWPAN_CANCELED; + break; + + case ILowpanInterface.ERROR_CREDENTIAL_NEEDED: + reason = LowpanException.LOWPAN_CREDENTIAL_NEEDED; + break; + + case ILowpanInterface.ERROR_FEATURE_NOT_SUPPORTED: + reason = LowpanException.LOWPAN_FEATURE_NOT_SUPPORTED; + break; + + case ILowpanInterface.ERROR_PROPERTY_NOT_FOUND: + reason = LowpanException.LOWPAN_PROPERTY_NOT_FOUND; + break; + + case ILowpanInterface.ERROR_JOIN_FAILED_UNKNOWN: + reason = LowpanException.LOWPAN_JOIN_FAILED_UNKNOWN; + break; + + case ILowpanInterface.ERROR_JOIN_FAILED_AT_SCAN: + reason = LowpanException.LOWPAN_JOIN_FAILED_AT_SCAN; + break; + + case ILowpanInterface.ERROR_JOIN_FAILED_AT_AUTH: + reason = LowpanException.LOWPAN_JOIN_FAILED_AT_AUTH; + break; + + case ILowpanInterface.ERROR_FORM_FAILED_AT_SCAN: + reason = LowpanException.LOWPAN_FORM_FAILED_AT_SCAN; + break; + + case ILowpanInterface.ERROR_TIMEOUT: + case ILowpanInterface.ERROR_NCP_PROBLEM: + reason = LowpanException.LOWPAN_NCP_PROBLEM; + break; + case ILowpanInterface.ERROR_UNSPECIFIED: + default: + reason = LOWPAN_ERROR; + break; + } + throw new LowpanException(reason, e.getMessage(), e); + } else if (t instanceof DeadObjectException) { + throw new LowpanException(LOWPAN_DEAD, t); + } else if (t instanceof RemoteException) { + throw new UnsupportedOperationException( + "An unknown RemoteException was thrown" + " which should never happen.", t); + } else if (t instanceof RuntimeException) { + RuntimeException e = (RuntimeException) t; + throw e; + } + } + + private final int mReason; + + public final int getReason() { + return mReason; + } + + public LowpanException(int problem) { + super(getDefaultMessage(problem)); + mReason = problem; + } + + public LowpanException(String message) { + super(getCombinedMessage(LOWPAN_ERROR, message)); + mReason = LOWPAN_ERROR; + } + + public LowpanException(int problem, String message, Throwable cause) { + super(getCombinedMessage(problem, message), cause); + mReason = problem; + } + + public LowpanException(int problem, Throwable cause) { + super(getDefaultMessage(problem), cause); + mReason = problem; + } + + /** @hide */ + public static String getDefaultMessage(int problem) { + String problemString; + + // TODO: Does this need localization? + + switch (problem) { + case LOWPAN_DEAD: + problemString = "LoWPAN interface is no longer alive"; + break; + case LOWPAN_DISABLED: + problemString = "LoWPAN interface is disabled"; + break; + case LOWPAN_WRONG_STATE: + problemString = "LoWPAN interface in wrong state to perfom requested action"; + break; + case LOWPAN_BUSY: + problemString = + "LoWPAN interface was unable to perform the requestion action because it was busy"; + break; + case LOWPAN_NCP_PROBLEM: + problemString = + "The Network Co-Processor associated with this interface has experienced a problem"; + break; + case LOWPAN_ALREADY: + problemString = "The LoWPAN interface is already in the given state"; + break; + case LOWPAN_CANCELED: + problemString = "This operation was canceled"; + break; + case LOWPAN_CREDENTIAL_NEEDED: + problemString = "Additional credentials are required to complete this operation"; + break; + case LOWPAN_FEATURE_NOT_SUPPORTED: + problemString = + "A dependent feature required to perform the given action is not currently supported"; + break; + case LOWPAN_PROPERTY_NOT_FOUND: + problemString = "The given property was not found"; + break; + case LOWPAN_JOIN_FAILED_UNKNOWN: + problemString = "The join operation failed for an unspecified reason"; + break; + case LOWPAN_JOIN_FAILED_AT_SCAN: + problemString = + "The join operation failed because it could not communicate with any peers"; + break; + case LOWPAN_JOIN_FAILED_AT_AUTH: + problemString = + "The join operation failed because the credentials were not accepted by any peers"; + break; + case LOWPAN_FORM_FAILED_AT_SCAN: + problemString = "Network form failed"; + break; + case LOWPAN_ERROR: + default: + problemString = "The requested LoWPAN operation failed"; + break; + } + + return problemString; + } + + private static String getCombinedMessage(int problem, String message) { + String problemString = getProblemString(problem); + return String.format("%s (%d): %s", problemString, problem, message); + } + + private static String getProblemString(int problem) { + String problemString; + + switch (problem) { + case LOWPAN_ERROR: + problemString = "LOWPAN_ERROR"; + break; + case LOWPAN_DEAD: + problemString = "LOWPAN_DEAD"; + break; + case LOWPAN_DISABLED: + problemString = "LOWPAN_DISABLED"; + break; + case LOWPAN_WRONG_STATE: + problemString = "LOWPAN_WRONG_STATE"; + break; + case LOWPAN_BUSY: + problemString = "LOWPAN_BUSY"; + break; + case LOWPAN_NCP_PROBLEM: + problemString = "LOWPAN_NCP_PROBLEM"; + break; + case LOWPAN_ALREADY: + problemString = "LOWPAN_ALREADY"; + break; + case LOWPAN_CANCELED: + problemString = "LOWPAN_CANCELED"; + break; + case LOWPAN_CREDENTIAL_NEEDED: + problemString = "LOWPAN_CREDENTIAL_NEEDED"; + break; + case LOWPAN_FEATURE_NOT_SUPPORTED: + problemString = "LOWPAN_FEATURE_NOT_SUPPORTED"; + break; + case LOWPAN_PROPERTY_NOT_FOUND: + problemString = "LOWPAN_PROPERTY_NOT_FOUND"; + break; + case LOWPAN_JOIN_FAILED_UNKNOWN: + problemString = "LOWPAN_JOIN_FAILED_UNKNOWN"; + break; + case LOWPAN_JOIN_FAILED_AT_SCAN: + problemString = "LOWPAN_JOIN_FAILED_AT_SCAN"; + break; + case LOWPAN_JOIN_FAILED_AT_AUTH: + problemString = "LOWPAN_JOIN_FAILED_AT_AUTH"; + break; + case LOWPAN_FORM_FAILED_AT_SCAN: + problemString = "LOWPAN_FORM_FAILED_AT_SCAN"; + break; + default: + problemString = "LOWPAN_ERROR_CODE_" + problem; + break; + } + + return problemString; + } +} diff --git a/lowpan/java/android/net/lowpan/LowpanIdentity.java b/lowpan/java/android/net/lowpan/LowpanIdentity.java new file mode 100644 index 000000000000..2e7b560fda5e --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanIdentity.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2017 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.net.lowpan; + +import com.android.internal.util.HexDump; +import java.util.Map; + +/** + * Describes an instance of a LoWPAN network. + * + * @hide + */ +//@SystemApi +public class LowpanIdentity { + + ////////////////////////////////////////////////////////////////////////// + // Constants + + /** @hide */ + public static final int TYPE_ZIGBEE = 1; + + /** @hide */ + public static final int TYPE_ZIGBEE_IP = 2; + + /** @hide */ + public static final int TYPE_THREAD = 3; + + public static final int UNKNOWN = Integer.MAX_VALUE; + + ////////////////////////////////////////////////////////////////////////// + // Builder + + /** @hide */ + //@SystemApi + public static class Builder { + private final LowpanIdentity identity = new LowpanIdentity(); + + public Builder setName(String x) { + identity.mName = x; + return this; + } + + public Builder setXpanid(byte x[]) { + identity.mXpanid = x.clone(); + return this; + } + + public Builder setPanid(int x) { + identity.mPanid = x; + return this; + } + + /** @hide */ + public Builder setType(int x) { + identity.mType = x; + return this; + } + + public Builder setChannel(int x) { + identity.mChannel = x; + return this; + } + + /** @hide */ + Builder updateFromMap(Map map) { + if (map.containsKey(ILowpanInterface.KEY_NETWORK_NAME)) { + setName(LowpanProperties.KEY_NETWORK_NAME.getFromMap(map)); + } + if (map.containsKey(ILowpanInterface.KEY_NETWORK_PANID)) { + setPanid(LowpanProperties.KEY_NETWORK_PANID.getFromMap(map)); + } + if (map.containsKey(ILowpanInterface.KEY_NETWORK_XPANID)) { + setXpanid(LowpanProperties.KEY_NETWORK_XPANID.getFromMap(map)); + } + if (map.containsKey(ILowpanInterface.KEY_CHANNEL)) { + setChannel(LowpanProperties.KEY_CHANNEL.getFromMap(map)); + } + if (map.containsKey(ILowpanInterface.KEY_NETWORK_TYPE)) { + setType(LowpanProperties.KEY_NETWORK_TYPE.getFromMap(map)); + } + return this; + } + + public LowpanIdentity build() { + return identity; + } + } + + LowpanIdentity() {} + + ////////////////////////////////////////////////////////////////////////// + // Instance Variables + + private String mName = null; + private byte[] mXpanid = null; + private int mType = UNKNOWN; + private int mPanid = UNKNOWN; + private int mChannel = UNKNOWN; + + ////////////////////////////////////////////////////////////////////////// + // Public Getters and Setters + + public String getName() { + return mName; + } + + public byte[] getXpanid() { + return mXpanid.clone(); + } + + public int getPanid() { + return mPanid; + } + + /** @hide */ + public int getType() { + return mType; + } + + public int getChannel() { + return mChannel; + } + + static void addToMap(Map<String, Object> parameters, LowpanIdentity networkInfo) { + if (networkInfo.getName() != null) { + LowpanProperties.KEY_NETWORK_NAME.putInMap(parameters, networkInfo.getName()); + } + if (networkInfo.getPanid() != LowpanIdentity.UNKNOWN) { + LowpanProperties.KEY_NETWORK_PANID.putInMap( + parameters, networkInfo.getPanid()); + } + if (networkInfo.getChannel() != LowpanIdentity.UNKNOWN) { + LowpanProperties.KEY_CHANNEL.putInMap( + parameters, networkInfo.getChannel()); + } + if (networkInfo.getXpanid() != null) { + LowpanProperties.KEY_NETWORK_XPANID.putInMap(parameters, networkInfo.getXpanid()); + } + } + + void addToMap(Map<String, Object> parameters) { + addToMap(parameters, this); + } + + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + + sb.append("Name: ").append(mName == null ? "<none>" : mName); + + if (mXpanid != null) { + sb.append(", XPANID: ").append(HexDump.toHexString(mXpanid)); + } + + if (mPanid != UNKNOWN) { + sb.append(", PANID: ").append(String.format("0x%04X", mPanid)); + } + + if (mChannel != UNKNOWN) { + sb.append(", Channel: ").append(mChannel); + } + + return sb.toString(); + } +} diff --git a/lowpan/java/android/net/lowpan/LowpanInterface.java b/lowpan/java/android/net/lowpan/LowpanInterface.java new file mode 100644 index 000000000000..cd548190fa17 --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanInterface.java @@ -0,0 +1,824 @@ +/* + * Copyright (C) 2017 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.net.lowpan; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.IpPrefix; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceSpecificException; +import android.util.Log; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * Class for managing a specific Low-power Wireless Personal Area Network (LoWPAN) interface. + * + * @hide + */ +//@SystemApi +public class LowpanInterface { + private static final String TAG = LowpanInterface.class.getSimpleName(); + + /** Detached role. The interface is not currently attached to a network. */ + public static final String ROLE_DETACHED = "detached"; + + /** End-device role. End devices do not route traffic for other nodes. */ + public static final String ROLE_END_DEVICE = "end-device"; + + /** Router role. Routers help route traffic around the mesh network. */ + public static final String ROLE_ROUTER = "router"; + + /** + * Sleepy End-Device role. + * + * <p>End devices with this role are nominally asleep, waking up periodically to check in with + * their parent to see if there are packets destined for them. Such devices are capable of + * extraordinarilly low power consumption, but packet latency can be on the order of dozens of + * seconds(depending on how the node is configured). + */ + public static final String ROLE_SLEEPY_END_DEVICE = "sleepy-end-device"; + + /** + * Sleepy-router role. + * + * <p>Routers with this role are nominally asleep, waking up periodically to check in with other + * routers and their children. + */ + public static final String ROLE_SLEEPY_ROUTER = "sleepy-router"; + + /** TODO: doc */ + public static final String ROLE_LEADER = "leader"; + + /** TODO: doc */ + public static final String ROLE_COORDINATOR = "coordinator"; + + /** + * Offline state. + * + * <p>This is the initial state of the LoWPAN interface when the underlying driver starts. In + * this state the NCP is idle and not connected to any network. + * + * <p>This state can be explicitly entered by calling {@link #reset()}, {@link #leave()}, or + * <code>setUp(false)</code>, with the later two only working if we were not previously in the + * {@link #STATE_FAULT} state. + * + * @see #getState() + * @see #STATE_FAULT + */ + public static final String STATE_OFFLINE = "offline"; + + /** + * Commissioning state. + * + * <p>The interface enters this state after a call to {@link #startCommissioningSession()}. This + * state may only be entered directly from the {@link #STATE_OFFLINE} state. + * + * @see #startCommissioningSession() + * @see #getState() + * @hide + */ + public static final String STATE_COMMISSIONING = "commissioning"; + + /** + * Attaching state. + * + * <p>The interface enters this state when it starts the process of trying to find other nodes + * so that it can attach to any pre-existing network fragment, or when it is in the process of + * calculating the optimal values for unspecified parameters when forming a new network. + * + * <p>The interface may stay in this state for a prolonged period of time (or may spontaneously + * enter this state from {@link #STATE_ATTACHED}) if the underlying network technology is + * heirarchical (like ZigBeeIP) or if the device role is that of an "end-device" ({@link + * #ROLE_END_DEVICE} or {@link #ROLE_SLEEPY_END_DEVICE}). This is because such roles cannot + * create their own network fragments. + * + * @see #STATE_ATTACHED + * @see #getState() + */ + public static final String STATE_ATTACHING = "attaching"; + + /** + * Attached state. + * + * <p>The interface enters this state from {@link #STATE_ATTACHING} once it is actively + * participating on a network fragment. + * + * @see #STATE_ATTACHING + * @see #getState() + */ + public static final String STATE_ATTACHED = "attached"; + + /** + * Fault state. + * + * <p>The interface will enter this state when the driver has detected some sort of problem from + * which it was not immediately able to recover. + * + * <p>This state can be entered spontaneously from any other state. Calling {@link #reset} will + * cause the device to return to the {@link #STATE_OFFLINE} state. + * + * @see #getState + * @see #STATE_OFFLINE + */ + public static final String STATE_FAULT = "fault"; + + /** + * Network type for Thread 1.x networks. + * + * @see android.net.lowpan.LowpanIdentity#getType + * @see #getLowpanIdentity + * @hide + */ + public static final String NETWORK_TYPE_THREAD = "org.threadgroup.thread.v1"; + + /** + * Network type for ZigBeeIP 1.x networks. + * + * @see android.net.lowpan.LowpanIdentity#getType + * @see #getLowpanIdentity + * @hide + */ + public static final String NETWORK_TYPE_ZIGBEE_IP = "org.zigbee.zigbeeip.v1"; + + private static final String NETWORK_PROPERTY_KEYS[] = { + LowpanProperties.KEY_NETWORK_NAME.getName(), + LowpanProperties.KEY_NETWORK_PANID.getName(), + LowpanProperties.KEY_NETWORK_XPANID.getName(), + LowpanProperties.KEY_CHANNEL.getName() + }; + + /** + * Callback base class for LowpanInterface + * + * @hide + */ + //@SystemApi + public abstract static class Callback { + public void onConnectedChanged(boolean value) {} + + public void onEnabledChanged(boolean value) {} + + public void onUpChanged(boolean value) {} + + public void onRoleChanged(@NonNull String value) {} + + public void onStateChanged(@NonNull String state) {} + + public void onLowpanIdentityChanged(@NonNull LowpanIdentity value) {} + + /** @hide */ + public void onPropertiesChanged(@NonNull Map properties) {} + } + + private ILowpanInterface mBinder; + private final HashMap<Integer, ILowpanInterfaceListener> mListenerMap = new HashMap<>(); + + /** Map between IBinder identity hashes and LowpanInstance objects. */ + private static final HashMap<Integer, LowpanInterface> sInstanceMap = new HashMap<>(); + + private LowpanInterface(IBinder binder) { + mBinder = ILowpanInterface.Stub.asInterface(binder); + } + + /** + * Get the LowpanInterface object associated with this IBinder. Returns null if this IBinder + * does not implement the appropriate interface. + * + * @hide + */ + @NonNull + public static final LowpanInterface from(IBinder binder) { + Integer hashCode = Integer.valueOf(System.identityHashCode(binder)); + LowpanInterface instance; + + synchronized (sInstanceMap) { + instance = sInstanceMap.get(hashCode); + + if (instance == null) { + instance = new LowpanInterface(binder); + sInstanceMap.put(hashCode, instance); + } + } + + return instance; + } + + /** {@hide} */ + public static final LowpanInterface from(ILowpanInterface iface) { + return from(iface.asBinder()); + } + + /** {@hide} */ + public static final LowpanInterface getInterfaceFromBinder(IBinder binder) { + return from(binder); + } + + /** + * Returns the IBinder object associated with this interface. + * + * @hide + */ + public IBinder getBinder() { + return mBinder.asBinder(); + } + + private static void throwAsPublicException(Throwable t) throws LowpanException { + LowpanException.throwAsPublicException(t); + } + + ////////////////////////////////////////////////////////////////////////// + // Private Property Helpers + + void setProperties(Map properties) throws LowpanException { + try { + mBinder.setProperties(properties); + + } catch (RemoteException x) { + // Catch and ignore all binder exceptions + Log.e(TAG, x.toString()); + + } catch (ServiceSpecificException x) { + throwAsPublicException(x); + } + } + + @NonNull + Map<String, Object> getProperties(String keys[]) throws LowpanException { + try { + return mBinder.getProperties(keys); + } catch (RemoteException x) { + // Catch and ignore all binder exceptions + Log.e(TAG, x.toString()); + } catch (ServiceSpecificException x) { + throwAsPublicException(x); + } + return new HashMap(); + } + + /** @hide */ + public <T> void setProperty(LowpanProperty<T> key, T value) throws LowpanException { + HashMap<String, T> prop = new HashMap<>(); + prop.put(key.getName(), value); + setProperties(prop); + } + + /** @hide */ + @Nullable + public <T> T getProperty(LowpanProperty<T> key) throws LowpanException { + Map<String, Object> map = getProperties(new String[] {key.getName()}); + if (map != null && !map.isEmpty()) { + // We know there is only one value. + return (T) map.values().iterator().next(); + } + return null; + } + + @Nullable + <T> String getPropertyAsString(LowpanProperty<T> key) throws LowpanException { + try { + return mBinder.getPropertyAsString(key.getName()); + } catch (RemoteException x) { + // Catch and ignore all binder exceptions + Log.e(TAG, x.toString()); + } catch (ServiceSpecificException x) { + throwAsPublicException(x); + } + return null; + } + + int getPropertyAsInt(LowpanProperty<Integer> key) throws LowpanException { + Integer value = getProperty(key); + return (value != null) ? value : 0; + } + + boolean getPropertyAsBoolean(LowpanProperty<Boolean> key) throws LowpanException { + Boolean value = getProperty(key); + return (value != null) ? value : 0; + } + + ////////////////////////////////////////////////////////////////////////// + // Public Actions + + /** + * Form a new network with the given network information optional credential. Unspecified fields + * in the network information will be filled in with reasonable values. If the network + * credential is unspecified, one will be generated automatically. + * + * <p>This method will block until either the network was successfully formed or an error + * prevents the network form being formed. + * + * <p>Upon success, the interface will be up and attached to the newly formed network. + * + * @see #join(LowpanProvision) + */ + public void form(@NonNull LowpanProvision provision) throws LowpanException { + try { + Map<String, Object> parameters = new HashMap(); + provision.addToMap(parameters); + mBinder.form(parameters); + } catch (RemoteException x) { + throwAsPublicException(x); + } catch (ServiceSpecificException x) { + throwAsPublicException(x); + } + } + + /** + * Attempts to join a new network with the given network information. This method will block + * until either the network was successfully joined or an error prevented the network from being + * formed. Upon success, the interface will be up and attached to the newly joined network. + * + * <p>Note that “joining” is distinct from “attaching”: Joining requires at least one other peer + * device to be present in order for the operation to complete successfully. + */ + public void join(@NonNull LowpanProvision provision) throws LowpanException { + try { + Map<String, Object> parameters = new HashMap(); + provision.addToMap(parameters); + mBinder.join(parameters); + } catch (RemoteException x) { + throwAsPublicException(x); + } catch (ServiceSpecificException x) { + throwAsPublicException(x); + } + } + + /** + * Attaches to the network described by identity and credential. This is similar to {@link + * #join}, except that (assuming the identity and credential are valid) it will always succeed + * and provision the interface, even if there are no peers nearby. + * + * <p>This method will block execution until the operation has completed. + */ + public void attach(@NonNull LowpanProvision provision) throws LowpanException { + if (ROLE_DETACHED.equals(getRole())) { + Map<String, Object> parameters = new HashMap(); + provision.addToMap(parameters); + setProperties(parameters); + setUp(true); + } else { + throw new LowpanException(LowpanException.LOWPAN_ALREADY); + } + } + + /** + * Bring down the network interface and forget all non-volatile details about the current + * network. + * + * <p>This method will block execution until the operation has completed. + */ + public void leave() throws LowpanException { + try { + mBinder.leave(); + } catch (RemoteException x) { + throwAsPublicException(x); + } catch (ServiceSpecificException x) { + throwAsPublicException(x); + } + } + + /** + * Start a new commissioning session. Will fail if the interface is attached to a network or if + * the interface is disabled. + */ + public @NonNull LowpanCommissioningSession startCommissioningSession( + @NonNull LowpanBeaconInfo beaconInfo) throws LowpanException { + + /* TODO: Implement startCommissioningSession */ + throw new LowpanException(LowpanException.LOWPAN_FEATURE_NOT_SUPPORTED); + } + + /** + * Reset this network interface as if it has been power cycled. Will bring the network interface + * down if it was previously up. Will not erase any non-volatile settings. + * + * <p>This method will block execution until the operation has completed. + * + * @hide + */ + public void reset() throws LowpanException { + try { + mBinder.reset(); + } catch (RemoteException x) { + throwAsPublicException(x); + } catch (ServiceSpecificException x) { + throwAsPublicException(x); + } + } + + ////////////////////////////////////////////////////////////////////////// + // Public Getters and Setters + + /** + * Returns the name of this network interface. + * + * <p>Will return empty string if this interface is no longer viable. + */ + @NonNull + public String getName() { + try { + return mBinder.getName(); + } catch (RemoteException x) { + // Catch and ignore all binder exceptions + // when fetching the name. + Log.e(TAG, x.toString()); + } catch (ServiceSpecificException x) { + // Catch and ignore all service-specific exceptions + // when fetching the name. + Log.e(TAG, x.toString()); + } + return ""; + } + + /** + * Indicates if the interface is enabled or disabled. + * + * @see #setEnabled + * @see android.net.lowpan.LowpanException#LOWPAN_DISABLED + */ + public boolean isEnabled() { + try { + return getPropertyAsBoolean(LowpanProperties.KEY_INTERFACE_ENABLED); + } catch (LowpanException x) { + return false; + } + } + + /** + * Enables or disables the LoWPAN interface. When disabled, the interface is put into a low-power + * state and all commands that require the NCP to be queried will fail with {@link + * android.net.lowpan.LowpanException#LOWPAN_DISABLED}. + * + * @see #isEnabled + * @see android.net.lowpan.LowpanException#LOWPAN_DISABLED + * @hide + */ + public void setEnabled(boolean enabled) throws LowpanException { + setProperty(LowpanProperties.KEY_INTERFACE_ENABLED, enabled); + } + + /** + * Indicates if the network interface is up or down. + * + * @hide + */ + public boolean isUp() { + try { + return getPropertyAsBoolean(LowpanProperties.KEY_INTERFACE_UP); + } catch (LowpanException x) { + return false; + } + } + + /** + * Bring up or shut down the network interface. + * + * <p>This method brings up or shuts down the network interface, attaching or (gracefully) + * detaching from the currently configured LoWPAN network as appropriate. + * + * @hide + */ + public void setUp(boolean interfaceUp) throws LowpanException { + setProperty(LowpanProperties.KEY_INTERFACE_UP, interfaceUp); + } + + /** + * Indicates if there is at least one peer in range. + * + * @return <code>true</code> if we have at least one other peer in range, <code>false</code> + * otherwise. + */ + public boolean isConnected() { + try { + return getPropertyAsBoolean(LowpanProperties.KEY_INTERFACE_CONNECTED); + } catch (LowpanException x) { + return false; + } + } + + /** + * Indicates if this interface is currently commissioned onto an existing network. If the + * interface is commissioned, the interface may be brought up using setUp(). + */ + public boolean isCommissioned() { + try { + return getPropertyAsBoolean(LowpanProperties.KEY_INTERFACE_COMMISSIONED); + } catch (LowpanException x) { + return false; + } + } + + /** + * Get interface state + * + * <h3>State Diagram</h3> + * + * <img src="LowpanInterface-1.png" /> + * + * @return The current state of the interface. + * @see #STATE_OFFLINE + * @see #STATE_COMMISSIONING + * @see #STATE_ATTACHING + * @see #STATE_ATTACHED + * @see #STATE_FAULT + */ + public String getState() { + try { + return getProperty(LowpanProperties.KEY_INTERFACE_STATE); + } catch (LowpanException x) { + Log.e(TAG, x.toString()); + return STATE_FAULT; + } + } + + /** TODO: doc */ + public LowpanIdentity getLowpanIdentity() { + LowpanIdentity.Builder builder = new LowpanIdentity.Builder(); + try { + builder.updateFromMap(getProperties(NETWORK_PROPERTY_KEYS)); + } catch (LowpanException x) { + // We ignore all LoWPAN-specitic exceptions here. + } + + return builder.build(); + } + + /** + * TODO: doc + * + * @hide + */ + public void setLowpanIdentity(LowpanIdentity network) throws LowpanException { + Map<String, Object> map = new HashMap(); + LowpanIdentity.addToMap(map, network); + setProperties(map); + } + + /** TODO: doc */ + @NonNull + public String getRole() { + String role = null; + + try { + role = getProperty(LowpanProperties.KEY_NETWORK_ROLE); + } catch (LowpanException x) { + // We ignore all LoWPAN-specitic exceptions here. + Log.e(TAG, x.toString()); + } + + if (role == null) { + role = ROLE_DETACHED; + } + + return role; + } + + /** TODO: doc */ + @Nullable + public LowpanCredential getLowpanCredential() { + LowpanCredential credential = null; + + try { + Integer keyIndex = getProperty(LowpanProperties.KEY_NETWORK_MASTER_KEY_INDEX); + + if (keyIndex == null) { + credential = + LowpanCredential.createMasterKey( + getProperty(LowpanProperties.KEY_NETWORK_MASTER_KEY)); + } else { + credential = + LowpanCredential.createMasterKey( + getProperty(LowpanProperties.KEY_NETWORK_MASTER_KEY), + keyIndex.intValue()); + } + } catch (LowpanException x) { + // We ignore all LoWPAN-specitic exceptions here. + Log.e(TAG, x.toString()); + } + + return credential; + } + + /** + * TODO: doc + * + * @hide + */ + public void setLowpanCredential(LowpanCredential networkCredential) throws LowpanException { + Map<String, Object> map = new HashMap(); + networkCredential.addToMap(map); + setProperties(map); + } + + ////////////////////////////////////////////////////////////////////////// + // Listener Support + + /** + * Registers a subclass of {@link LowpanInterface.Callback} to receive events. + * + * @param cb Subclass of {@link LowpanInterface.Callback} which will receive events. + * @param handler If not <code>null</code>, events will be dispatched via the given handler + * object. If <code>null</code>, the thread upon which events will be dispatched is + * unspecified. + * @see #registerCallback(Callback) + * @see #unregisterCallback(Callback) + */ + public void registerCallback(@NonNull Callback cb, @Nullable Handler handler) { + ILowpanInterfaceListener.Stub listenerBinder = + new ILowpanInterfaceListener.Stub() { + public void onPropertiesChanged(Map<String, Object> properties) { + Runnable runnable = + new Runnable() { + @Override + public void run() { + for (String key : (Set<String>) properties.keySet()) { + Object value = properties.get(key); + switch (key) { + case ILowpanInterface.KEY_INTERFACE_ENABLED: + cb.onEnabledChanged( + ((Boolean) value).booleanValue()); + break; + case ILowpanInterface.KEY_INTERFACE_UP: + cb.onUpChanged( + ((Boolean) value).booleanValue()); + break; + case ILowpanInterface.KEY_INTERFACE_CONNECTED: + cb.onConnectedChanged( + ((Boolean) value).booleanValue()); + break; + case ILowpanInterface.KEY_INTERFACE_STATE: + cb.onStateChanged((String) value); + break; + case ILowpanInterface.KEY_NETWORK_NAME: + case ILowpanInterface.KEY_NETWORK_PANID: + case ILowpanInterface.KEY_NETWORK_XPANID: + case ILowpanInterface.KEY_CHANNEL: + cb.onLowpanIdentityChanged(getLowpanIdentity()); + break; + case ILowpanInterface.KEY_NETWORK_ROLE: + cb.onRoleChanged(value.toString()); + break; + } + } + cb.onPropertiesChanged(properties); + } + }; + + if (handler != null) { + handler.post(runnable); + } else { + runnable.run(); + } + } + }; + try { + mBinder.addListener(listenerBinder); + } catch (RemoteException x) { + // Log and ignore. If this happens, this interface + // is likely dead anyway. + Log.e(TAG, x.toString()); + } + synchronized (mListenerMap) { + mListenerMap.put(System.identityHashCode(cb), listenerBinder); + } + } + + /** + * Registers a subclass of {@link LowpanInterface.Callback} to receive events. + * + * <p>The thread upon which events will be dispatched is unspecified. + * + * @param cb Subclass of {@link LowpanInterface.Callback} which will receive events. + * @see #registerCallback(Callback, Handler) + * @see #unregisterCallback(Callback) + */ + public void registerCallback(Callback cb) { + registerCallback(cb, null); + } + + /** + * Unregisters a previously registered callback class. + * + * @param cb Subclass of {@link LowpanInterface.Callback} which was previously registered to + * receive events. + * @see #registerCallback(Callback, Handler) + * @see #registerCallback(Callback) + */ + public void unregisterCallback(Callback cb) { + int hashCode = System.identityHashCode(cb); + ILowpanInterfaceListener listenerBinder = mListenerMap.get(hashCode); + + if (listenerBinder != null) { + synchronized (mListenerMap) { + mListenerMap.remove(hashCode); + } + try { + mBinder.removeListener(listenerBinder); + } catch (RemoteException x) { + // Catch and ignore all binder exceptions + Log.e(TAG, x.toString()); + } + } + } + + ////////////////////////////////////////////////////////////////////////// + // Active and Passive Scanning + + /** + * Creates a new {@link android.net.lowpan.LowpanScanner} object for this interface. + * + * <p>This method allocates a new unique object for each call. + * + * @see android.net.lowpan.LowpanScanner + */ + public @NonNull LowpanScanner createScanner() { + return new LowpanScanner(mBinder); + } + + ////////////////////////////////////////////////////////////////////////// + // Route Management + + /** + * Advertise the given IP prefix as an on-mesh prefix. + * + * @hide + */ + public void addOnMeshPrefix(IpPrefix prefix, int flags) throws LowpanException { + try { + mBinder.addOnMeshPrefix(prefix, flags); + } catch (RemoteException x) { + throwAsPublicException(x); + } catch (ServiceSpecificException x) { + throwAsPublicException(x); + } + } + + /** + * Remove an IP prefix previously advertised by this device from the list of advertised on-mesh + * prefixes. + * + * @hide + */ + public void removeOnMeshPrefix(IpPrefix prefix) { + try { + mBinder.removeOnMeshPrefix(prefix); + } catch (RemoteException x) { + // Catch and ignore all binder exceptions + Log.e(TAG, x.toString()); + } catch (ServiceSpecificException x) { + // Catch and ignore all service exceptions + Log.e(TAG, x.toString()); + } + } + + /** + * Advertise this device to other devices on the mesh network as having a specific route to the + * given network. This device will then receive forwarded traffic for that network. + * + * @hide + */ + public void addExternalRoute(IpPrefix prefix, int flags) throws LowpanException { + try { + mBinder.addExternalRoute(prefix, flags); + } catch (RemoteException x) { + throwAsPublicException(x); + } catch (ServiceSpecificException x) { + throwAsPublicException(x); + } + } + + /** + * Revoke a previously advertised specific route to the given network. + * + * @hide + */ + public void removeExternalRoute(IpPrefix prefix) { + try { + mBinder.removeExternalRoute(prefix); + } catch (RemoteException x) { + // Catch and ignore all binder exceptions + Log.e(TAG, x.toString()); + } catch (ServiceSpecificException x) { + // Catch and ignore all service exceptions + Log.e(TAG, x.toString()); + } + } +} diff --git a/lowpan/java/android/net/lowpan/LowpanManager.java b/lowpan/java/android/net/lowpan/LowpanManager.java new file mode 100644 index 000000000000..b58608da7c21 --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanManager.java @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2017 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.net.lowpan; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.os.DeadObjectException; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.AndroidException; +import android.util.Log; +import java.util.HashMap; + +/** + * Manager object for looking up LoWPAN interfaces. + * + * @hide + */ +//@SystemApi +public class LowpanManager { + private static final String TAG = LowpanManager.class.getSimpleName(); + + ////////////////////////////////////////////////////////////////////////// + // Public Classes + + /** @hide */ + //@SystemApi + public abstract static class Callback { + public void onInterfaceAdded(LowpanInterface lowpan_interface) {} + + public void onInterfaceRemoved(LowpanInterface lowpan_interface) {} + } + + ////////////////////////////////////////////////////////////////////////// + // Instance Variables + + private ILowpanManager mManager; + private HashMap<Integer, ILowpanManagerListener> mListenerMap = new HashMap<>(); + + ////////////////////////////////////////////////////////////////////////// + + private static LowpanManager sSingletonInstance; + + ////////////////////////////////////////////////////////////////////////// + // Static Methods + + /** Returns a reference to the LowpanManager object, allocating it if necessary. */ + public static LowpanManager getManager() { + return from(null); + } + + public static LowpanManager from(Context context) { + // TODO: Actually get this from the context! + + if (sSingletonInstance == null) { + sSingletonInstance = new LowpanManager(); + } + return sSingletonInstance; + } + + ////////////////////////////////////////////////////////////////////////// + // Constructors + + /** + * Private LowpanManager constructor. Since we are a singleton, we do not allow external + * construction. + */ + private LowpanManager() {} + + ////////////////////////////////////////////////////////////////////////// + // Private Methods + + /** + * Returns a reference to the ILowpanManager interface, provided by the LoWPAN Manager Service. + */ + @Nullable + private ILowpanManager getILowpanManager() { + ILowpanManager manager = mManager; + if (manager == null) { + IBinder serviceBinder = + new ServiceManager().getService(ILowpanManager.LOWPAN_SERVICE_NAME); + mManager = manager = ILowpanManager.Stub.asInterface(serviceBinder); + + // Add any listeners + synchronized (mListenerMap) { + for (Integer hashObj : mListenerMap.keySet()) { + try { + manager.addListener(mListenerMap.get(hashObj)); + } catch (RemoteException x) { + // Consider any failure here as implying the manager is defunct + mManager = manager = null; + } + } + } + } + return manager; + } + + ////////////////////////////////////////////////////////////////////////// + // Public Methods + + /** + * Returns a reference to the requested LowpanInterface object. If the given interface doesn't + * exist, or it is not a LoWPAN interface, returns null. + */ + @Nullable + public LowpanInterface getInterface(@NonNull String name) { + LowpanInterface ret = null; + ILowpanManager manager = getILowpanManager(); + + // Maximum number of tries is two. We should only try + // more than once if our manager has died or there + // was some sort of AIDL buffer full event. + for (int i = 0; i < 2 && manager != null; i++) { + try { + ILowpanInterface iface = manager.getInterface(name); + if (iface != null) { + ret = LowpanInterface.getInterfaceFromBinder(iface.asBinder()); + } + break; + } catch (RemoteException x) { + // In all of the cases when we get this exception, we reconnect and try again + mManager = null; + manager = getILowpanManager(); + } + } + return ret; + } + + /** + * Returns a reference to the first registered LowpanInterface object. If there are no LoWPAN + * interfaces registered, returns null. + */ + @Nullable + public LowpanInterface getInterface() { + String[] ifaceList = getInterfaceList(); + if (ifaceList != null && ifaceList.length > 0) { + return getInterface(ifaceList[0]); + } + return null; + } + + /** + * Returns a string array containing the names of LoWPAN interfaces. This list may contain fewer + * interfaces if the calling process does not have permissions to see individual interfaces. + */ + @NonNull + public String[] getInterfaceList() { + ILowpanManager manager = getILowpanManager(); + + if (manager != null) { + try { + return manager.getInterfaceList(); + + } catch (RemoteException x) { + // In all of the cases when we get this exception, we reconnect and try again + mManager = null; + try { + manager = getILowpanManager(); + if (manager != null) { + return manager.getInterfaceList(); + } + } catch (RemoteException ex) { + // Something weird is going on, so we log it + // and fall back thru to returning an empty array. + Log.e(TAG, ex.toString()); + mManager = null; + } + } + } + + // Return empty list if we have no service. + return new String[0]; + } + + /** + * Registers a callback object to receive notifications when LoWPAN interfaces are added or + * removed. + * + * @hide + */ + public void registerCallback(@NonNull Callback cb, @Nullable Handler handler) + throws LowpanException { + ILowpanManagerListener.Stub listenerBinder = + new ILowpanManagerListener.Stub() { + public void onInterfaceAdded(ILowpanInterface lowpan_interface) { + Runnable runnable = + new Runnable() { + @Override + public void run() { + cb.onInterfaceAdded( + LowpanInterface.getInterfaceFromBinder( + lowpan_interface.asBinder())); + } + }; + + if (handler != null) { + handler.post(runnable); + } else { + runnable.run(); + } + } + + public void onInterfaceRemoved(ILowpanInterface lowpan_interface) { + Runnable runnable = + new Runnable() { + @Override + public void run() { + cb.onInterfaceRemoved( + LowpanInterface.getInterfaceFromBinder( + lowpan_interface.asBinder())); + } + }; + + if (handler != null) { + handler.post(runnable); + } else { + runnable.run(); + } + } + }; + ILowpanManager manager = getILowpanManager(); + if (manager != null) { + try { + manager.addListener(listenerBinder); + } catch (DeadObjectException x) { + mManager = null; + // Tickle the ILowpanManager instance, which might + // get us added back. + getILowpanManager(); + } catch (Throwable x) { + LowpanException.throwAsPublicException(x); + } + } + synchronized (mListenerMap) { + mListenerMap.put(Integer.valueOf(System.identityHashCode(cb)), listenerBinder); + } + } + + /** @hide */ + public void registerCallback(@NonNull Callback cb) throws LowpanException { + registerCallback(cb, null); + } + + /** + * Unregisters a previously registered {@link LowpanManager.Callback} object. + * + * @hide + */ + public void unregisterCallback(@NonNull Callback cb) throws AndroidException { + Integer hashCode = Integer.valueOf(System.identityHashCode(cb)); + ILowpanManagerListener listenerBinder = mListenerMap.get(hashCode); + if (listenerBinder != null) { + synchronized (mListenerMap) { + mListenerMap.remove(hashCode); + } + if (getILowpanManager() != null) { + try { + mManager.removeListener(listenerBinder); + } catch (DeadObjectException x) { + mManager = null; + } + } + } + } +} diff --git a/lowpan/java/android/net/lowpan/LowpanProperties.java b/lowpan/java/android/net/lowpan/LowpanProperties.java new file mode 100644 index 000000000000..0d5acc2d0c21 --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanProperties.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2017 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.net.lowpan; + +import android.net.LinkAddress; +import android.net.RouteInfo; +import java.util.List; + +/** {@hide} */ +public final class LowpanProperties { + + public static final LowpanProperty<Boolean> KEY_INTERFACE_ENABLED = + new LowpanStandardProperty( + "android.net.lowpan.property.INTERFACE_ENABLED", Boolean.class); + public static final LowpanProperty<Boolean> KEY_INTERFACE_COMMISSIONED = + new LowpanStandardProperty( + "android.net.lowpan.property.INTERFACE_COMMISSIONED", Boolean.class); + public static final LowpanProperty<Boolean> KEY_INTERFACE_CONNECTED = + new LowpanStandardProperty( + "android.net.lowpan.property.INTERFACE_CONNECTED", Boolean.class); + public static final LowpanProperty<Boolean> KEY_INTERFACE_UP = + new LowpanStandardProperty("android.net.lowpan.property.INTERFACE_UP", Boolean.class); + public static final LowpanProperty<String> KEY_INTERFACE_STATE = + new LowpanStandardProperty("android.net.lowpan.property.INTERFACE_STATE", String.class); + + public static final LowpanProperty<String> KEY_NETWORK_NAME = + new LowpanStandardProperty("android.net.lowpan.property.NETWORK_NAME", Boolean.class); + public static final LowpanProperty<Integer> KEY_NETWORK_PANID = + new LowpanStandardProperty("android.net.lowpan.property.NETWORK_PANID", Integer.class); + public static final LowpanProperty<byte[]> KEY_NETWORK_XPANID = + new LowpanStandardProperty("android.net.lowpan.property.NETWORK_XPANID", byte[].class); + public static final LowpanProperty<byte[]> KEY_NETWORK_MASTER_KEY = + new LowpanStandardProperty( + "android.net.lowpan.property.NETWORK_MASTER_KEY", byte[].class); + public static final LowpanProperty<Integer> KEY_NETWORK_MASTER_KEY_INDEX = + new LowpanStandardProperty( + "android.net.lowpan.property.NETWORK_MASTER_KEY_INDEX", Integer.class); + public static final LowpanProperty<Integer> KEY_NETWORK_TYPE = + new LowpanStandardProperty("android.net.lowpan.property.NETWORK_TYPE", Integer.class); + public static final LowpanProperty<String> KEY_NETWORK_ROLE = + new LowpanStandardProperty("android.net.lowpan.property.NETWORK_ROLE", String.class); + + public static final LowpanProperty<Integer> KEY_CHANNEL = + new LowpanStandardProperty("android.net.lowpan.property.CHANNEL", Integer.class); + public static final LowpanProperty<int[]> KEY_CHANNEL_MASK = + new LowpanStandardProperty("android.net.lowpan.property.CHANNEL_MASK", int[].class); + public static final LowpanProperty<Integer> KEY_MAX_TX_POWER = + new LowpanStandardProperty("android.net.lowpan.property.MAX_TX_POWER", Integer.class); + public static final LowpanProperty<Integer> KEY_RSSI = + new LowpanStandardProperty("android.net.lowpan.property.RSSI", Integer.class); + + public static final LowpanProperty<Integer> KEY_LQI = + new LowpanStandardProperty("android.net.lowpan.property.LQI", Integer.class); + public static final LowpanProperty<byte[]> KEY_BEACON_ADDRESS = + new LowpanStandardProperty("android.net.lowpan.property.BEACON_ADDRESS", byte[].class); + public static final LowpanProperty<Boolean> KEY_BEACON_CAN_ASSIST = + new LowpanStandardProperty( + "android.net.lowpan.property.BEACON_CAN_ASSIST", Boolean.class); + + public static final LowpanProperty<String> KEY_DRIVER_VERSION = + new LowpanStandardProperty("android.net.lowpan.property.DRIVER_VERSION", String.class); + + public static final LowpanProperty<String> KEY_NCP_VERSION = + new LowpanStandardProperty("android.net.lowpan.property.NCP_VERSION", String.class); + + public static final LowpanProperty<List<LinkAddress>> KEY_LINK_ADDRESS_ARRAY = + new LowpanStandardProperty( + "android.net.lowpan.property.LINK_ADDRESS_ARRAY", LinkAddress[].class); + + public static final LowpanProperty<List<RouteInfo>> KEY_ROUTE_INFO_ARRAY = + new LowpanStandardProperty( + "android.net.lowpan.property.ROUTE_INFO_ARRAY", RouteInfo[].class); + + /** @hide */ + public static final LowpanProperty<byte[]> KEY_EXTENDED_ADDRESS = + new LowpanStandardProperty( + "android.net.lowpan.property.EXTENDED_ADDRESS", byte[].class); + + /** @hide */ + public static final LowpanProperty<byte[]> KEY_MAC_ADDRESS = + new LowpanStandardProperty("android.net.lowpan.property.MAC_ADDRESS", byte[].class); + + /** @hide */ + private LowpanProperties() {} + + /** @hide */ + static final class LowpanStandardProperty<T> extends LowpanProperty<T> { + private final String mName; + private final Class<T> mType; + + LowpanStandardProperty(String name, Class<T> type) { + mName = name; + mType = type; + } + + @Override + public String getName() { + return mName; + } + + @Override + public Class<T> getType() { + return mType; + } + + @Override + public String toString() { + return getName(); + } + } +} diff --git a/lowpan/java/android/net/lowpan/LowpanProperty.java b/lowpan/java/android/net/lowpan/LowpanProperty.java new file mode 100644 index 000000000000..7f26986e9da0 --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanProperty.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2017 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.net.lowpan; + +import java.util.Map; + +/** {@hide} */ +public abstract class LowpanProperty<T> { + public abstract String getName(); + + public abstract Class<T> getType(); + + public void putInMap(Map map, T value) { + map.put(getName(), value); + } + + public T getFromMap(Map map) { + return (T) map.get(getName()); + } +} diff --git a/lowpan/java/android/net/lowpan/LowpanProvision.java b/lowpan/java/android/net/lowpan/LowpanProvision.java new file mode 100644 index 000000000000..ace1f9c05c4d --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanProvision.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2017 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.net.lowpan; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import java.util.Map; + +/** + * Describes the information needed to describe a network + * + * @hide + */ +//@SystemApi +public class LowpanProvision { + + ////////////////////////////////////////////////////////////////////////// + // Builder + + /** @hide */ + //@SystemApi + public static class Builder { + private final LowpanProvision provision = new LowpanProvision(); + + public Builder setLowpanIdentity(@NonNull LowpanIdentity identity) { + provision.mIdentity = identity; + return this; + } + + public Builder setLowpanCredential(@NonNull LowpanCredential credential) { + provision.mCredential = credential; + return this; + } + + public LowpanProvision build() { + return provision; + } + } + + private LowpanProvision() {} + + ////////////////////////////////////////////////////////////////////////// + // Instance Variables + + private LowpanIdentity mIdentity = new LowpanIdentity(); + private LowpanCredential mCredential = null; + + ////////////////////////////////////////////////////////////////////////// + // Public Getters and Setters + + @NonNull + public LowpanIdentity getLowpanIdentity() { + return mIdentity; + } + + @Nullable + public LowpanCredential getLowpanCredential() { + return mCredential; + } + + ////////////////////////////////////////////////////////////////////////// + // LoWPAN-Internal Methods + + static void addToMap(Map<String, Object> parameters, LowpanProvision provision) + throws LowpanException { + provision.mIdentity.addToMap(parameters); + if (provision.mCredential != null) { + provision.mCredential.addToMap(parameters); + } + } + + void addToMap(Map<String, Object> parameters) throws LowpanException { + addToMap(parameters, this); + } + + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + + sb.append("LowpanProvision { identity => ").append(mIdentity.toString()); + + if (mCredential != null) { + sb.append(", credential: ").append(mCredential.toString()); + } + + sb.append("}"); + + return sb.toString(); + } +}; diff --git a/lowpan/java/android/net/lowpan/LowpanScanner.java b/lowpan/java/android/net/lowpan/LowpanScanner.java new file mode 100644 index 000000000000..754f72e3cb84 --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanScanner.java @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2017 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.net.lowpan; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Handler; +import android.os.RemoteException; +import android.os.ServiceSpecificException; +import android.util.Log; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * LoWPAN Scanner + * + * <p>This class allows performing network (active) scans and energy (passive) scans. + * + * @see LowpanInterface + * @hide + */ +//@SystemApi +public class LowpanScanner { + private static final String TAG = LowpanInterface.class.getSimpleName(); + + ////////////////////////////////////////////////////////////////////////// + // Public Classes + + /** + * Callback base class for LowpanScanner + * + * @hide + */ + //@SystemApi + public abstract static class Callback { + public void onNetScanBeacon(LowpanBeaconInfo beacon) {} + + public void onEnergyScanResult(LowpanEnergyScanResult result) {} + + public void onScanFinished() {} + } + + ////////////////////////////////////////////////////////////////////////// + // Instance Variables + + private ILowpanInterface mBinder; + private Callback mCallback = null; + private Handler mHandler = null; + private List<Integer> mChannelMask = null; + private int mTxPower = Integer.MAX_VALUE; + + ////////////////////////////////////////////////////////////////////////// + // Constructors/Accessors and Exception Glue + + LowpanScanner(@NonNull ILowpanInterface binder) { + mBinder = binder; + } + + /** Sets an instance of {@link LowpanScanner.Callback} to receive events. */ + public void setCallback(@Nullable Callback cb, @Nullable Handler handler) { + mCallback = cb; + mHandler = handler; + } + + /** Sets an instance of {@link LowpanScanner.Callback} to receive events. */ + public void setCallback(@Nullable Callback cb) { + setCallback(cb, null); + } + + /** + * Sets the channel mask to use when scanning. + * + * @param mask The channel mask to use when scanning. If <code>null</code>, any previously set + * channel mask will be cleared and all channels not masked by the current regulatory zone + * will be scanned. + */ + public void setChannelMask(@Nullable Collection<Integer> mask) { + if (mask == null) { + mChannelMask = null; + } else { + if (mChannelMask == null) { + mChannelMask = new ArrayList<>(); + } else { + mChannelMask.clear(); + } + mChannelMask.addAll(mask); + } + } + + /** + * Gets the current channel mask. + * + * @return the current channel mask, or <code>null</code> if no channel mask is currently set. + */ + public @Nullable Collection<Integer> getChannelMask() { + return mChannelMask.clone(); + } + + /** + * Adds a channel to the channel mask used for scanning. + * + * <p>If a channel mask was previously <code>null</code>, a new one is created containing only + * this channel. May be called multiple times to add additional channels ot the channel mask. + * + * @see #setChannelMask + * @see #getChannelMask + * @see #getTxPower + */ + public void addChannel(int channel) { + if (mChannelMask == null) { + mChannelMask = new ArrayList<>(); + } + mChannelMask.add(Integer.valueOf(channel)); + } + + /** + * Sets the maximum transmit power to be used for active scanning. + * + * <p>The actual transmit power used is the lesser of this value and the currently configured + * maximum transmit power for the interface. + * + * @see #getTxPower + */ + public void setTxPower(int txPower) { + mTxPower = txPower; + } + + /** + * Gets the maximum transmit power used for active scanning. + * + * @see #setTxPower + */ + public int getTxPower() { + return mTxPower; + } + + private Map<String, Object> createScanOptionMap() { + Map<String, Object> map = new HashMap(); + + if (mChannelMask != null) { + LowpanProperties.KEY_CHANNEL_MASK.putInMap( + map, mChannelMask.stream().mapToInt(i -> i).toArray()); + } + + if (mTxPower != Integer.MAX_VALUE) { + LowpanProperties.KEY_MAX_TX_POWER.putInMap(map, Integer.valueOf(mTxPower)); + } + + return map; + } + + /** + * Start a network scan. + * + * <p>This method will return once the scan has started. + * + * @see #stopNetScan + */ + public void startNetScan() throws LowpanException { + Map<String, Object> map = createScanOptionMap(); + + ILowpanNetScanCallback binderListener = + new ILowpanNetScanCallback.Stub() { + public void onNetScanBeacon(Map parameters) { + Callback callback = mCallback; + Handler handler = mHandler; + + if (callback == null) { + return; + } + + Runnable runnable = () -> callback.onNetScanBeacon( + new LowpanBeaconInfo.Builder() + .updateFromMap(parameters) + .build()); + + if (handler != null) { + handler.post(runnable); + } else { + runnable.run(); + } + } + + public void onNetScanFinished() { + Callback callback = mCallback; + Handler handler = mHandler; + + if (callback == null) { + return; + } + + Runnable runnable = () -> callback.onScanFinished(); + + if (handler != null) { + handler.post(runnable); + } else { + runnable.run(); + } + } + }; + + try { + mBinder.startNetScan(map, binderListener); + } catch (ServiceSpecificException|RemoteException x) { + LowpanException.throwAsPublicException(x); + } + } + + /** + * Stop a network scan currently in progress. + * + * @see #startNetScan + */ + public void stopNetScan() { + try { + mBinder.stopNetScan(); + } catch (RemoteException x) { + // Catch and ignore all binder exceptions + Log.e(TAG, x.toString()); + } + } + + /** + * Start an energy scan. + * + * <p>This method will return once the scan has started. + * + * @see #stopEnergyScan + */ + public void startEnergyScan() throws LowpanException { + Map<String, Object> map = createScanOptionMap(); + + ILowpanEnergyScanCallback binderListener = + new ILowpanEnergyScanCallback.Stub() { + public void onEnergyScanResult(int channel, int rssi) { + Callback callback = mCallback; + Handler handler = mHandler; + + if (callback == null) { + return; + } + + Runnable runnable = () -> { + if (callback != null) { + LowpanEnergyScanResult result = + new LowpanEnergyScanResult(); + result.setChannel(channel); + result.setMaxRssi(rssi); + callback.onEnergyScanResult(result); + } + }; + + if (handler != null) { + handler.post(runnable); + } else { + runnable.run(); + } + } + + public void onEnergyScanFinished() { + Callback callback = mCallback; + Handler handler = mHandler; + + if (callback == null) { + return; + } + + Runnable runnable = () -> callback.onScanFinished(); + + if (handler != null) { + handler.post(runnable); + } else { + runnable.run(); + } + } + }; + + try { + mBinder.startEnergyScan(map, binderListener); + } catch (RemoteException x) { + LowpanException.throwAsPublicException(x); + } catch (ServiceSpecificException x) { + LowpanException.throwAsPublicException(x); + } + } + + /** + * Stop an energy scan currently in progress. + * + * @see #startEnergyScan + */ + public void stopEnergyScan() { + try { + mBinder.stopEnergyScan(); + } catch (RemoteException x) { + // Catch and ignore all binder exceptions + Log.e(TAG, x.toString()); + } + } +} diff --git a/lowpan/java/android/net/lowpan/package.html b/lowpan/java/android/net/lowpan/package.html new file mode 100644 index 000000000000..342e32eefc1e --- /dev/null +++ b/lowpan/java/android/net/lowpan/package.html @@ -0,0 +1,29 @@ +<HTML> +<BODY> +<p>@SystemApi</p> +<!-- @hide --> +<p>Provides classes to manage Low-power Wireless Personal Area Network (LoWPAN) functionality on the device. +Examples of such network technologies include <a href="http://threadgroup.org/">Thread</a> and +<a href="http://www.zigbee.org/zigbee-for-developers/network-specifications/zigbeeip/">ZigBee IP</a>.</p> +<p>The LoWPAN APIs provide a means by which applications can communicate +with the lower-level wireless stack that provides LoWPAN network access.</p> + +<p>Some APIs may require the following user permissions:</p> +<ul> + <li>{@link android.Manifest.permission#ACCESS_LOWPAN_STATE}</li> + <li>{@link android.Manifest.permission#CHANGE_LOWPAN_STATE}</li> + <li>TBD</li> +</ul> + +<p class="note"><strong>Note:</strong> Not all Android-powered devices provide LoWPAN functionality. +If your application uses these APIs, declare so with a <a +href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code <uses-feature>}</a> +element in the manifest file:</p> +<pre> +<manifest ...> + <uses-feature android:name="android.hardware.lowpan" /> + ... +</manifest> +</pre> +</BODY> +</HTML> diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 0b5dff227d23..20deeb1634c8 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -19,9 +19,11 @@ package android.media; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.app.NotificationManager; import android.app.PendingIntent; import android.bluetooth.BluetoothDevice; @@ -58,10 +60,8 @@ import java.util.concurrent.ConcurrentHashMap; /** * AudioManager provides access to volume and ringer mode control. - * <p> - * Use <code>Context.getSystemService(Context.AUDIO_SERVICE)</code> to get - * an instance of this class. */ +@SystemService(Context.AUDIO_SERVICE) public class AudioManager { private Context mOriginalContext; @@ -2827,6 +2827,7 @@ public class AudioManager { * {@link #SUCCESS} otherwise. */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull AudioPolicy policy) { if (policy == null) { throw new IllegalArgumentException("Illegal null AudioPolicy argument"); @@ -2852,6 +2853,7 @@ public class AudioManager { * @param policy the non-null {@link AudioPolicy} to unregister. */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicyAsync(@NonNull AudioPolicy policy) { if (policy == null) { throw new IllegalArgumentException("Illegal null AudioPolicy argument"); diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java index 228a6de6907c..dd731df0fbb0 100644 --- a/media/java/android/media/MediaHTTPConnection.java +++ b/media/java/android/media/MediaHTTPConnection.java @@ -136,7 +136,13 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { private void teardownConnection() { if (mConnection != null) { - mInputStream = null; + if (mInputStream != null) { + try { + mInputStream.close(); + } catch (IOException e) { + } + mInputStream = null; + } mConnection.disconnect(); mConnection = null; @@ -298,8 +304,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { mCurrentOffset = offset; } catch (IOException e) { mTotalSize = -1; - mInputStream = null; - mConnection = null; + teardownConnection(); mCurrentOffset = -1; throw e; diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index 87d8c64ead94..d5509c14e09b 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -20,6 +20,7 @@ import android.Manifest; import android.annotation.DrawableRes; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.SystemService; import android.app.ActivityThread; import android.content.BroadcastReceiver; import android.content.Context; @@ -61,6 +62,7 @@ import java.util.concurrent.CopyOnWriteArrayList; * <p>The media router API is not thread-safe; all interactions with it must be * done from the main thread of the process.</p> */ +@SystemService(Context.MEDIA_ROUTER_SERVICE) public class MediaRouter { private static final String TAG = "MediaRouter"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); diff --git a/media/java/android/media/midi/MidiManager.java b/media/java/android/media/midi/MidiManager.java index 07c8ae86adcf..a015732ddfdd 100644 --- a/media/java/android/media/midi/MidiManager.java +++ b/media/java/android/media/midi/MidiManager.java @@ -16,7 +16,9 @@ package android.media.midi; +import android.annotation.SystemService; import android.bluetooth.BluetoothDevice; +import android.content.Context; import android.os.Binder; import android.os.IBinder; import android.os.Bundle; @@ -28,13 +30,8 @@ import java.util.concurrent.ConcurrentHashMap; /** * This class is the public application interface to the MIDI service. - * - * <p>You can obtain an instance of this class by calling - * {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}. - * - * {@samplecode - * MidiManager manager = (MidiManager) getSystemService(Context.MIDI_SERVICE);} */ +@SystemService(Context.MIDI_SERVICE) public final class MidiManager { private static final String TAG = "MidiManager"; diff --git a/media/java/android/media/projection/MediaProjectionManager.java b/media/java/android/media/projection/MediaProjectionManager.java index f4a548bb5329..9f2c08e5c6ae 100644 --- a/media/java/android/media/projection/MediaProjectionManager.java +++ b/media/java/android/media/projection/MediaProjectionManager.java @@ -18,6 +18,7 @@ package android.media.projection; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemService; import android.app.Activity; import android.content.Context; import android.content.Intent; @@ -33,14 +34,8 @@ import java.util.Map; /** * Manages the retrieval of certain types of {@link MediaProjection} tokens. - * - * <p> - * Get an instance of this class by calling {@link - * android.content.Context#getSystemService(java.lang.String) - * Context.getSystemService()} with the argument {@link - * android.content.Context#MEDIA_PROJECTION_SERVICE}. - * </p> */ +@SystemService(Context.MEDIA_PROJECTION_SERVICE) public final class MediaProjectionManager { private static final String TAG = "MediaProjectionManager"; /** @hide */ diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index 83793aeb2466..e02a4dcba9e4 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -18,7 +18,9 @@ package android.media.session; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.content.ComponentName; import android.content.Context; import android.media.AudioManager; @@ -42,13 +44,11 @@ import java.util.List; * Provides support for interacting with {@link MediaSession media sessions} * that applications have published to express their ongoing media playback * state. - * <p> - * Use <code>Context.getSystemService(Context.MEDIA_SESSION_SERVICE)</code> to - * get an instance of this class. * * @see MediaSession * @see MediaController */ +@SystemService(Context.MEDIA_SESSION_SERVICE) public final class MediaSessionManager { private static final String TAG = "SessionManager"; @@ -357,6 +357,7 @@ public final class MediaSessionManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER) public void setOnVolumeKeyLongPressListener( OnVolumeKeyLongPressListener listener, @Nullable Handler handler) { synchronized (mLock) { @@ -392,6 +393,7 @@ public final class MediaSessionManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.SET_MEDIA_KEY_LISTENER) public void setOnMediaKeyListener(OnMediaKeyListener listener, @Nullable Handler handler) { synchronized (mLock) { try { diff --git a/media/java/android/media/soundtrigger/SoundTriggerDetector.java b/media/java/android/media/soundtrigger/SoundTriggerDetector.java index c091c84df657..7969ee757813 100644 --- a/media/java/android/media/soundtrigger/SoundTriggerDetector.java +++ b/media/java/android/media/soundtrigger/SoundTriggerDetector.java @@ -20,6 +20,7 @@ import static android.hardware.soundtrigger.SoundTrigger.STATUS_OK; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.hardware.soundtrigger.IRecognitionStatusCallback; import android.hardware.soundtrigger.SoundTrigger; @@ -253,6 +254,7 @@ public final class SoundTriggerDetector { * {@link Callback}. * @return Indicates whether the call succeeded or not. */ + @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public boolean startRecognition(@RecognitionFlags int recognitionFlags) { if (DBG) { Slog.d(TAG, "startRecognition()"); @@ -276,6 +278,7 @@ public final class SoundTriggerDetector { /** * Stops recognition for the associated model. */ + @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public boolean stopRecognition() { int status = STATUS_OK; try { diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java index fdd7fc2a8f19..7f8140adaed2 100644 --- a/media/java/android/media/soundtrigger/SoundTriggerManager.java +++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java @@ -18,7 +18,9 @@ package android.media.soundtrigger; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.content.Context; import android.hardware.soundtrigger.SoundTrigger; import android.os.Handler; @@ -39,6 +41,7 @@ import java.util.UUID; * @hide */ @SystemApi +@SystemService(Context.SOUND_TRIGGER_SERVICE) public final class SoundTriggerManager { private static final boolean DBG = false; private static final String TAG = "SoundTriggerManager"; @@ -65,6 +68,7 @@ public final class SoundTriggerManager { /** * Updates the given sound trigger model. */ + @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public void updateModel(Model model) { try { mSoundTriggerService.updateSoundModel(model.getGenericSoundModel()); @@ -77,6 +81,7 @@ public final class SoundTriggerManager { * Returns the sound trigger model represented by the given UUID. An instance of {@link Model} * is returned. */ + @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public Model getModel(UUID soundModelId) { try { return new Model(mSoundTriggerService.getSoundModel( @@ -89,6 +94,7 @@ public final class SoundTriggerManager { /** * Deletes the sound model represented by the provided UUID. */ + @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public void deleteModel(UUID soundModelId) { try { mSoundTriggerService.deleteSoundModel(new ParcelUuid(soundModelId)); @@ -110,6 +116,7 @@ public final class SoundTriggerManager { * @return Instance of {@link SoundTriggerDetector} or null on error. */ @Nullable + @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public SoundTriggerDetector createSoundTriggerDetector(UUID soundModelId, @NonNull SoundTriggerDetector.Callback callback, @Nullable Handler handler) { if (soundModelId == null) { diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index 68ee02cab7a4..28fd338aabc7 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -21,6 +21,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.content.Context; import android.content.Intent; import android.graphics.Rect; import android.media.PlaybackParams; @@ -57,9 +59,7 @@ import java.util.Map; /** * Central system API to the overall TV input framework (TIF) architecture, which arbitrates - * interaction between applications and the selected TV inputs. You can retrieve an instance of - * this interface with {@link android.content.Context#getSystemService - * Context.getSystemService(Context.TV_INPUT_SERVICE)}. + * interaction between applications and the selected TV inputs. * * <p>There are three primary parties involved in the TV input framework (TIF) architecture: * @@ -78,6 +78,7 @@ import java.util.Map; * programs. * </ul> */ +@SystemService(Context.TV_INPUT_SERVICE) public final class TvInputManager { private static final String TAG = "TvInputManager"; @@ -1516,6 +1517,7 @@ public final class TvInputManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId) { try { return mService.getAvailableTvStreamConfigList(inputId, mUserId); @@ -1534,6 +1536,7 @@ public final class TvInputManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public boolean captureFrame(String inputId, Surface surface, TvStreamConfig config) { try { return mService.captureFrame(inputId, surface, config, mUserId); diff --git a/packages/CarrierDefaultApp/res/values/strings.xml b/packages/CarrierDefaultApp/res/values/strings.xml index fa5c3fff283f..65a7cecca20f 100644 --- a/packages/CarrierDefaultApp/res/values/strings.xml +++ b/packages/CarrierDefaultApp/res/values/strings.xml @@ -6,6 +6,8 @@ <string name="no_data_notification_id">Your mobile data has been deactivated</string> <string name="portal_notification_detail">Tap to visit the %s website</string> <string name="no_data_notification_detail">Please contact your service provider %s</string> + <string name="no_mobile_data_connection_title">No mobile data connection</string> + <string name="no_mobile_data_connection">Add data or roaming plan through %s</string> <string name="mobile_data_status_notification_channel_name">Mobile data status</string> <string name="action_bar_label">Sign in to mobile network</string> <string name="ssl_error_warning">The network you’re trying to join has security issues.</string> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 021b68bc9501..fd994b572981 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -64,6 +64,14 @@ <!-- Status for networks disabled from authentication failure (wrong password or certificate). --> <string name="wifi_disabled_password_failure">Authentication problem</string> + + <!-- Status detail for a network that can't be connected to for some reason --> + <string name="wifi_cant_connect">Can\'t connect</string> + <!-- Status for a named network that can't be connected to for some reason--> + <string name="wifi_cant_connect_to_ap">Can\'t connect to \'<xliff:g id="ap_name">%1$s</xliff:g>\'</string> + <!-- Message shown when the user likely entered an incorrect password for a wifi network --> + <string name="wifi_check_password_try_again">Check password and try again</string> + <!-- Summary for the remembered network but currently not in range. --> <string name="wifi_not_in_range">Not in range</string> <!-- Summary for the network but no internet connection was detected. --> @@ -94,7 +102,7 @@ <!-- Speed label for slow network speed --> <string name="speed_label_slow">Slow</string> <!-- Speed label for okay network speed --> - <string name="speed_label_okay">Okay</string> + <string name="speed_label_okay">OK</string> <!-- Speed label for medium network speed --> <string name="speed_label_medium">Medium</string> <!-- Speed label for fast network speed --> diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index 3135f1db957d..b21f2fa860b1 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -21,6 +21,7 @@ import android.net.NetworkBadging; import android.os.BatteryManager; import android.os.UserManager; import android.print.PrintManager; +import android.provider.Settings; import android.view.View; import com.android.internal.util.UserIcons; @@ -313,4 +314,20 @@ public class Utils { "No badge resource found for badge value: " + badge); } } + + public static int getDefaultStorageManagerDaysToRetain(Resources resources) { + int defaultDays = Settings.Secure.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_DEFAULT; + try { + defaultDays = + resources.getInteger( + com.android + .internal + .R + .integer + .config_storageManagerDaystoRetainDefault); + } catch (Resources.NotFoundException e) { + // We are likely in a test environment. + } + return defaultDays; + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java b/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java index 82da9a38d756..e4e0f7f9e361 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java @@ -26,13 +26,13 @@ public class InterestingConfigChanges { private int mLastDensity; public InterestingConfigChanges() { - this(0); + this(ActivityInfo.CONFIG_LOCALE + | ActivityInfo.CONFIG_UI_MODE | ActivityInfo.CONFIG_SCREEN_LAYOUT + | ActivityInfo.CONFIG_ASSETS_PATHS); } - public InterestingConfigChanges(int extraFlags) { - mFlags = extraFlags | ActivityInfo.CONFIG_LOCALE - | ActivityInfo.CONFIG_UI_MODE | ActivityInfo.CONFIG_SCREEN_LAYOUT - | ActivityInfo.CONFIG_ASSETS_PATHS; + public InterestingConfigChanges(int flags) { + mFlags = flags; } public boolean applyNewConfig(Resources res) { diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index c55280dc1c3e..0f9b2ff4d5f2 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -23,7 +23,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.net.ConnectivityManager; -import android.net.NetworkBadging; import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; @@ -83,6 +82,35 @@ public class AccessPoint implements Comparable<AccessPoint> { */ public static final int HIGHER_FREQ_5GHZ = 5900; + /** + * Constant value representing an unlabeled / unscored network. + */ + @VisibleForTesting + static final int SPEED_NONE = 0; + + /** + * Constant value representing a slow speed network connection. + */ + @VisibleForTesting + static final int SPEED_SLOW = 5; + + /** + * Constant value representing a medium speed network connection. + */ + @VisibleForTesting + static final int SPEED_MEDIUM = 10; + + /** + * Constant value representing a fast speed network connection. + */ + @VisibleForTesting + static final int SPEED_FAST = 20; + + /** + * Constant value representing a very fast speed network connection. + */ + @VisibleForTesting + static final int SPEED_VERY_FAST = 30; /** * Experimental: we should be able to show the user the list of BSSIDs and bands @@ -149,7 +177,7 @@ public class AccessPoint implements Comparable<AccessPoint> { private Object mTag; private int mRankingScore = Integer.MIN_VALUE; - private int mBadge = NetworkBadging.BADGING_NONE; + private int mSpeed = AccessPoint.SPEED_NONE; private boolean mIsScoredNetworkMetered = false; // used to co-relate internal vs returned accesspoint. @@ -249,7 +277,7 @@ public class AccessPoint implements Comparable<AccessPoint> { this.mScanResultCache.clear(); this.mScanResultCache.putAll(that.mScanResultCache); this.mId = that.mId; - this.mBadge = that.mBadge; + this.mSpeed = that.mSpeed; this.mIsScoredNetworkMetered = that.mIsScoredNetworkMetered; this.mRankingScore = that.mRankingScore; } @@ -340,8 +368,8 @@ public class AccessPoint implements Comparable<AccessPoint> { if (mRankingScore != Integer.MIN_VALUE) { builder.append(",rankingScore=").append(mRankingScore); } - if (mBadge != NetworkBadging.BADGING_NONE) { - builder.append(",badge=").append(mBadge); + if (mSpeed != SPEED_NONE) { + builder.append(",speed=").append(mSpeed); } builder.append(",metered=").append(isMetered()); @@ -349,7 +377,7 @@ public class AccessPoint implements Comparable<AccessPoint> { } /** - * Updates the AccessPoint rankingScore, metering, and badge, returning true if the data has + * Updates the AccessPoint rankingScore, metering, and speed, returning true if the data has * changed. * * @param scoreCache The score cache to use to retrieve scores. @@ -364,14 +392,14 @@ public class AccessPoint implements Comparable<AccessPoint> { } /** - * Updates the AccessPoint rankingScore and badge, returning true if the data has changed. + * Updates the AccessPoint rankingScore and speed, returning true if the data has changed. * * @param scoreCache The score cache to use to retrieve scores. */ private boolean updateScores(WifiNetworkScoreCache scoreCache) { - int oldBadge = mBadge; + int oldSpeed = mSpeed; int oldRankingScore = mRankingScore; - mBadge = NetworkBadging.BADGING_NONE; + mSpeed = SPEED_NONE; mRankingScore = Integer.MIN_VALUE; for (ScanResult result : mScanResultCache.values()) { @@ -383,10 +411,11 @@ public class AccessPoint implements Comparable<AccessPoint> { if (score.hasRankingScore()) { mRankingScore = Math.max(mRankingScore, score.calculateRankingScore(result.level)); } - mBadge = Math.max(mBadge, score.calculateBadge(result.level)); + // TODO(sghuman): Rename calculateBadge API + mSpeed = Math.max(mSpeed, score.calculateBadge(result.level)); } - return (oldBadge != mBadge || oldRankingScore != mRankingScore); + return (oldSpeed != mSpeed || oldRankingScore != mRankingScore); } /** @@ -643,7 +672,7 @@ public class AccessPoint implements Comparable<AccessPoint> { // TODO(b/62354743): Standardize and international delimiter usage final String concatenator = " / "; - if (mBadge != NetworkBadging.BADGING_NONE) { + if (mSpeed != SPEED_NONE) { summary.append(getSpeedLabel() + concatenator); } @@ -764,7 +793,7 @@ public class AccessPoint implements Comparable<AccessPoint> { if (mRankingScore != Integer.MIN_VALUE) { visibility.append(" rankingScore=").append(getRankingScore()); } - if (mBadge != NetworkBadging.BADGING_NONE) { + if (mSpeed != SPEED_NONE) { visibility.append(" speed=").append(getSpeedLabel()); } visibility.append(String.format(" tx=%.1f,", mInfo.txSuccessRate)); @@ -1062,18 +1091,20 @@ public class AccessPoint implements Comparable<AccessPoint> { return mRankingScore; } - int getBadge() { return mBadge;} + int getSpeed() { return mSpeed;} @Nullable String getSpeedLabel() { - switch (mBadge) { - case NetworkBadging.BADGING_4K: + switch (mSpeed) { + case SPEED_VERY_FAST: return mContext.getString(R.string.speed_label_very_fast); - case NetworkBadging.BADGING_HD: + case SPEED_FAST: return mContext.getString(R.string.speed_label_fast); - case NetworkBadging.BADGING_SD: + case SPEED_MEDIUM: return mContext.getString(R.string.speed_label_okay); - case NetworkBadging.BADGING_NONE: + case SPEED_SLOW: + return mContext.getString(R.string.speed_label_slow); + case SPEED_NONE: default: return null; } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java index f37590ec8c63..e7525f3edbc4 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java @@ -60,7 +60,7 @@ public class AccessPointPreference extends Preference { private int mLevel; private CharSequence mContentDescription; private int mDefaultIconResId; - private int mWifiBadge = NetworkBadging.BADGING_NONE; + private int mWifiSpeed = NetworkBadging.BADGING_NONE; static final int[] WIFI_CONNECTION_STRENGTH = { R.string.accessibility_no_wifi, @@ -161,7 +161,7 @@ public class AccessPointPreference extends Preference { safeSetDefaultIcon(); return; } - TronUtils.logWifiSettingsBadge(context, mWifiBadge); + TronUtils.logWifiSettingsBadge(context, mWifiSpeed); // TODO(b/62355275): Revert this to N code after deleting NetworkBadging API Drawable drawable = NetworkBadging.getWifiIcon( @@ -223,10 +223,10 @@ public class AccessPointPreference extends Preference { final Context context = getContext(); int level = mAccessPoint.getLevel(); - int wifiBadge = mAccessPoint.getBadge(); - if (level != mLevel || wifiBadge != mWifiBadge) { + int wifiSpeed = mAccessPoint.getSpeed(); + if (level != mLevel || wifiSpeed != mWifiSpeed) { mLevel = level; - mWifiBadge = wifiBadge; + mWifiSpeed = wifiSpeed; updateIcon(mLevel, context); notifyChanged(); } diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java index 9abdf88d42fb..72ac54421220 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java @@ -24,7 +24,6 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.net.ConnectivityManager; -import android.net.NetworkBadging; import android.net.NetworkInfo; import android.net.NetworkKey; import android.net.RssiCurve; @@ -330,57 +329,72 @@ public class AccessPointTest { } @Test - public void testSpeedLabel_returnsVeryFastWhen4kBadgeIsSet() { + public void testSpeedLabel_returnsVeryFast() { AccessPoint ap = createAccessPointWithScanResultCache(); when(mockWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class))) .thenReturn(buildScoredNetworkWithMockBadgeCurve()); - when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) NetworkBadging.BADGING_4K); + when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) AccessPoint.SPEED_VERY_FAST); ap.update(mockWifiNetworkScoreCache, true /* scoringUiEnabled */); - assertThat(ap.getBadge()).isEqualTo(NetworkBadging.BADGING_4K); + assertThat(ap.getSpeed()).isEqualTo(AccessPoint.SPEED_VERY_FAST); assertThat(ap.getSpeedLabel()) .isEqualTo(mContext.getString(R.string.speed_label_very_fast)); } @Test - public void testSpeedLabel_returnsFastWhenHdBadgeIsSet() { + public void testSpeedLabel_returnsFast() { AccessPoint ap = createAccessPointWithScanResultCache(); when(mockWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class))) .thenReturn(buildScoredNetworkWithMockBadgeCurve()); - when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) NetworkBadging.BADGING_HD); + when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) AccessPoint.SPEED_FAST); ap.update(mockWifiNetworkScoreCache, true /* scoringUiEnabled */); - assertThat(ap.getBadge()).isEqualTo(NetworkBadging.BADGING_HD); + assertThat(ap.getSpeed()).isEqualTo(AccessPoint.SPEED_FAST); assertThat(ap.getSpeedLabel()) .isEqualTo(mContext.getString(R.string.speed_label_fast)); } @Test - public void testSpeedLabel_returnsOkayWhenSdBadgeIsSet() { + public void testSpeedLabel_returnsOkay() { AccessPoint ap = createAccessPointWithScanResultCache(); when(mockWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class))) .thenReturn(buildScoredNetworkWithMockBadgeCurve()); - when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) NetworkBadging.BADGING_SD); + when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) AccessPoint.SPEED_MEDIUM); ap.update(mockWifiNetworkScoreCache, true /* scoringUiEnabled */); - assertThat(ap.getBadge()).isEqualTo(NetworkBadging.BADGING_SD); + assertThat(ap.getSpeed()).isEqualTo(AccessPoint.SPEED_MEDIUM); assertThat(ap.getSpeedLabel()) .isEqualTo(mContext.getString(R.string.speed_label_okay)); } @Test + public void testSpeedLabel_returnsSlow() { + AccessPoint ap = createAccessPointWithScanResultCache(); + + when(mockWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class))) + .thenReturn(buildScoredNetworkWithMockBadgeCurve()); + when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) AccessPoint.SPEED_SLOW); + + ap.update(mockWifiNetworkScoreCache, true /* scoringUiEnabled */); + + assertThat(ap.getSpeed()).isEqualTo(AccessPoint.SPEED_SLOW); + assertThat(ap.getSpeedLabel()) + .isEqualTo(mContext.getString(R.string.speed_label_slow)); + } + + @Test public void testSummaryString_showsSpeedLabel() { AccessPoint ap = createAccessPointWithScanResultCache(); when(mockWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class))) .thenReturn(buildScoredNetworkWithMockBadgeCurve()); - when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) NetworkBadging.BADGING_4K); + when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) AccessPoint.SPEED_VERY_FAST); ap.update(mockWifiNetworkScoreCache, true /* scoringUiEnabled */); @@ -394,7 +408,7 @@ public class AccessPointTest { when(mockWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class))) .thenReturn(buildScoredNetworkWithMockBadgeCurve()); - when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) NetworkBadging.BADGING_4K); + when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) AccessPoint.SPEED_VERY_FAST); ap.update(mockWifiNetworkScoreCache, true /* scoringUiEnabled */); diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java index 086e10cda499..46ea31993c38 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java @@ -37,7 +37,6 @@ import android.content.Context; import android.content.Intent; import android.net.ConnectivityManager; import android.net.Network; -import android.net.NetworkBadging; import android.net.NetworkInfo; import android.net.NetworkKey; import android.net.NetworkScoreManager; @@ -94,7 +93,7 @@ public class WifiTrackerTest { new NetworkKey(new WifiKey('"' + SSID_1 + '"', BSSID_1)); private static final int RSSI_1 = -30; private static final byte SCORE_1 = 10; - private static final int BADGE_1 = NetworkBadging.BADGING_SD; + private static final int BADGE_1 = AccessPoint.SPEED_MEDIUM; private static final String SSID_2 = "ssid2"; private static final String BSSID_2 = "AA:AA:AA:AA:AA:AA"; @@ -102,7 +101,7 @@ public class WifiTrackerTest { new NetworkKey(new WifiKey('"' + SSID_2 + '"', BSSID_2)); private static final int RSSI_2 = -30; private static final byte SCORE_2 = 15; - private static final int BADGE_2 = NetworkBadging.BADGING_HD; + private static final int BADGE_2 = AccessPoint.SPEED_FAST; private static final int CONNECTED_NETWORK_ID = 123; private static final int CONNECTED_RSSI = -50; @@ -511,7 +510,8 @@ public class WifiTrackerTest { } @Test - public void scoreCacheUpdateScoresShouldInsertBadgeIntoAccessPoint() throws InterruptedException { + public void scoreCacheUpdateScoresShouldInsertSpeedIntoAccessPoint() + throws InterruptedException { WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(); updateScoresAndWaitForAccessPointsChangedCallback(); @@ -519,9 +519,9 @@ public class WifiTrackerTest { for (AccessPoint ap : aps) { if (ap.getSsidStr().equals(SSID_1)) { - assertEquals(BADGE_1, ap.getBadge()); + assertEquals(BADGE_1, ap.getSpeed()); } else if (ap.getSsidStr().equals(SSID_2)) { - assertEquals(BADGE_2, ap.getBadge()); + assertEquals(BADGE_2, ap.getSpeed()); } } } @@ -544,7 +544,7 @@ public class WifiTrackerTest { } @Test - public void noBadgesShouldBeInsertedIntoAccessPointWhenScoringUiDisabled() + public void noSpeedsShouldBeInsertedIntoAccessPointWhenScoringUiDisabled() throws InterruptedException { Settings.Global.putInt( InstrumentationRegistry.getTargetContext().getContentResolver(), @@ -558,9 +558,9 @@ public class WifiTrackerTest { for (AccessPoint ap : aps) { if (ap.getSsidStr().equals(SSID_1)) { - assertEquals(NetworkBadging.BADGING_NONE, ap.getBadge()); + assertEquals(AccessPoint.SPEED_NONE, ap.getSpeed()); } else if (ap.getSsidStr().equals(SSID_2)) { - assertEquals(NetworkBadging.BADGING_NONE, ap.getBadge()); + assertEquals(AccessPoint.SPEED_NONE, ap.getSpeed()); } } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java index e42c35bc8d8b..0164f80e68f8 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java @@ -21,6 +21,12 @@ import org.robolectric.annotation.Config; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.content.res.Resources; + @RunWith(SettingLibRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) public class UtilsTest { @@ -54,4 +60,18 @@ public class UtilsTest { assertThat(percentage).isEqualTo(expectedPercentages[i]); } } + + @Test + public void testStorageManagerDaysToRetainUsesResources() { + Resources resources = mock(Resources.class); + when(resources.getInteger( + eq( + com.android + .internal + .R + .integer + .config_storageManagerDaystoRetainDefault))) + .thenReturn(60); + assertThat(Utils.getDefaultStorageManagerDaysToRetain(resources)).isEqualTo(60); + } } diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 5197c95ce884..73a2a43adb8f 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -272,6 +272,9 @@ public class Dependency extends SystemUI { mProviders.put(TunablePaddingService.class, () -> new TunablePaddingService()); + mProviders.put(ForegroundServiceController.class, + () -> new ForegroundServiceControllerImpl(mContext)); + mProviders.put(UiOffloadThread.class, UiOffloadThread::new); // Put all dependencies above here so the factory can override them if it wants. diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java new file mode 100644 index 000000000000..a2c9ab4871c2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2017 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; + +import android.service.notification.StatusBarNotification; + +public interface ForegroundServiceController { + /** + * @param sbn notification that was just posted + * @param importance + */ + void addNotification(StatusBarNotification sbn, int importance); + + /** + * @param sbn notification that was just changed in some way + * @param newImportance + */ + void updateNotification(StatusBarNotification sbn, int newImportance); + + /** + * @param sbn notification that was just canceled + */ + boolean removeNotification(StatusBarNotification sbn); + + /** + * @param userId + * @return true if this user has services missing notifications and therefore needs a + * disclosure notification. + */ + boolean isDungeonNeededForUser(int userId); + + /** + * @param sbn + * @return true if sbn is the system-provided "dungeon" (list of running foreground services). + */ + boolean isDungeonNotification(StatusBarNotification sbn); +} diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java new file mode 100644 index 000000000000..c930d567254a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceControllerImpl.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2017 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; + +import android.app.Notification; +import android.app.NotificationManager; +import android.content.Context; +import android.os.Bundle; +import android.service.notification.StatusBarNotification; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Log; +import android.util.SparseArray; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.messages.nano.SystemMessageProto; + +import java.util.Arrays; + +/** + * Foreground service controller, a/k/a Dianne's Dungeon. + */ +public class ForegroundServiceControllerImpl + implements ForegroundServiceController { + private static final String TAG = "FgServiceController"; + private static final boolean DBG = false; + + private final SparseArray<UserServices> mUserServices = new SparseArray<>(); + private final Object mMutex = new Object(); + + public ForegroundServiceControllerImpl(Context context) { + } + + @Override + public boolean isDungeonNeededForUser(int userId) { + synchronized (mMutex) { + final UserServices services = mUserServices.get(userId); + if (services == null) return false; + return services.isDungeonNeeded(); + } + } + + @Override + public void addNotification(StatusBarNotification sbn, int importance) { + updateNotification(sbn, importance); + } + + @Override + public boolean removeNotification(StatusBarNotification sbn) { + synchronized (mMutex) { + final UserServices userServices = mUserServices.get(sbn.getUserId()); + if (userServices == null) { + if (DBG) { + Log.w(TAG, String.format( + "user %d with no known notifications got removeNotification for %s", + sbn.getUserId(), sbn)); + } + return false; + } + if (isDungeonNotification(sbn)) { + // if you remove the dungeon entirely, we take that to mean there are + // no running services + userServices.setRunningServices(null); + return true; + } else { + // this is safe to call on any notification, not just FLAG_FOREGROUND_SERVICE + return userServices.removeNotification(sbn.getPackageName(), sbn.getKey()); + } + } + } + + @Override + public void updateNotification(StatusBarNotification sbn, int newImportance) { + synchronized (mMutex) { + UserServices userServices = mUserServices.get(sbn.getUserId()); + if (userServices == null) { + userServices = new UserServices(); + mUserServices.put(sbn.getUserId(), userServices); + } + + if (isDungeonNotification(sbn)) { + final Bundle extras = sbn.getNotification().extras; + if (extras != null) { + final String[] svcs = extras.getStringArray(Notification.EXTRA_FOREGROUND_APPS); + userServices.setRunningServices(svcs); // null ok + } + } else { + userServices.removeNotification(sbn.getPackageName(), sbn.getKey()); + if (0 != (sbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) + && newImportance > NotificationManager.IMPORTANCE_MIN) { + userServices.addNotification(sbn.getPackageName(), sbn.getKey()); + } + } + } + } + + @Override + public boolean isDungeonNotification(StatusBarNotification sbn) { + return sbn.getId() == SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICES + && sbn.getTag() == null + && sbn.getPackageName().equals("android"); + } + + /** + * Struct to track relevant packages and notifications for a userid's foreground services. + */ + private static class UserServices { + private String[] mRunning = null; + private ArrayMap<String, ArraySet<String>> mNotifications = new ArrayMap<>(1); + public void setRunningServices(String[] pkgs) { + mRunning = pkgs != null ? Arrays.copyOf(pkgs, pkgs.length) : null; + } + public void addNotification(String pkg, String key) { + if (mNotifications.get(pkg) == null) { + mNotifications.put(pkg, new ArraySet<String>()); + } + mNotifications.get(pkg).add(key); + } + public boolean removeNotification(String pkg, String key) { + final boolean found; + final ArraySet<String> keys = mNotifications.get(pkg); + if (keys == null) { + found = false; + } else { + found = keys.remove(key); + if (keys.size() == 0) { + mNotifications.remove(pkg); + } + } + return found; + } + public boolean isDungeonNeeded() { + if (mRunning != null) { + for (String pkg : mRunning) { + final ArraySet<String> set = mNotifications.get(pkg); + if (set == null || set.size() == 0) { + return true; + } + } + } + return false; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index 09fdf5acf032..3b81cc4801c2 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java @@ -92,7 +92,9 @@ public class AssistManager implements ConfigurationChangedReceiver { mAssistDisclosure = new AssistDisclosure(context, new Handler()); registerVoiceInteractionSessionListener(); - mInterestingConfigChanges = new InterestingConfigChanges(ActivityInfo.CONFIG_ORIENTATION); + mInterestingConfigChanges = new InterestingConfigChanges(ActivityInfo.CONFIG_ORIENTATION + | ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_UI_MODE + | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS); onConfigurationChanged(context.getResources().getConfiguration()); } diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java index 4dbf6f98e3e6..871f113ac95e 100644 --- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java +++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java @@ -50,7 +50,8 @@ public class FragmentHostManager { private final HashMap<String, ArrayList<FragmentListener>> mListeners = new HashMap<>(); private final View mRootView; private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges( - ActivityInfo.CONFIG_FONT_SCALE); + ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE + | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS); private final FragmentService mManager; private final ExtensionFragmentManager mPlugins = new ExtensionFragmentManager(); diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index 5414aad0a63f..f98310dd7aa1 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -239,7 +239,11 @@ public class PipManager implements BasePipManager { } } - loadConfigurationsAndApply(mContext.getResources().getConfiguration()); + // Initialize the last orientation and apply the current configuration + Configuration initialConfig = mContext.getResources().getConfiguration(); + mLastOrientation = initialConfig.orientation; + loadConfigurationsAndApply(initialConfig); + mMediaSessionManager = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java index 682c56c0ef24..488fc03032fd 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java @@ -18,12 +18,14 @@ package com.android.systemui.qs; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_DATE; +import android.app.ActivityManager; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.PorterDuff.Mode; import android.graphics.drawable.Drawable; import android.graphics.drawable.RippleDrawable; import android.os.UserManager; @@ -42,6 +44,7 @@ import android.widget.Toast; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.keyguard.KeyguardStatusView; +import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.FontSizeUtils; import com.android.systemui.R; @@ -423,6 +426,13 @@ public class QSFooter extends FrameLayout implements @Override public void onUserInfoChanged(String name, Drawable picture, String userAccount) { + if (picture != null && + UserManager.get(mContext).isGuestUser(ActivityManager.getCurrentUser())) { + picture = picture.getConstantState().newDrawable().mutate(); + picture.setColorFilter( + Utils.getColorAttr(mContext, android.R.attr.colorForeground), + Mode.SRC_IN); + } mMultiUserAvatar.setImageDrawable(picture); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java index d12b16b77ae5..672f2c2df06e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java @@ -317,7 +317,9 @@ public abstract class QSTileImpl<TState extends State> implements QSTile { protected abstract void setListening(boolean listening); protected void handleDestroy() { - setListening(false); + if (mListeners.size() != 0) { + setListening(false); + } mCallbacks.clear(); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/AnimationProps.java b/packages/SystemUI/src/com/android/systemui/recents/views/AnimationProps.java index 5e87e2a75c80..716d1bcf78c2 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/AnimationProps.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/AnimationProps.java @@ -54,7 +54,6 @@ public class AnimationProps { public static final int FOCUS_STATE = 8; private SparseLongArray mPropStartDelay; - private SparseLongArray mPropInitialPlayTime; private SparseLongArray mPropDuration; private SparseArray<Interpolator> mPropInterpolators; private Animator.AnimatorListener mListener; @@ -122,10 +121,6 @@ public class AnimationProps { animator.setStartDelay(getStartDelay(propertyType)); animator.setDuration(getDuration(propertyType)); animator.setInterpolator(getInterpolator(propertyType)); - long initialPlayTime = getInitialPlayTime(propertyType); - if (initialPlayTime != 0) { - animator.setCurrentPlayTime(initialPlayTime); - } return animator; } @@ -141,17 +136,6 @@ public class AnimationProps { } /** - * Sets a initial play time for a specific property. - */ - public AnimationProps setInitialPlayTime(@PropType int propertyType, int initialPlayTime) { - if (mPropInitialPlayTime == null) { - mPropInitialPlayTime = new SparseLongArray(); - } - mPropInitialPlayTime.append(propertyType, initialPlayTime); - return this; - } - - /** * Returns the start delay for a specific property. */ public long getStartDelay(@PropType int propertyType) { @@ -217,20 +201,6 @@ public class AnimationProps { } /** - * Returns the initial play time for a specific property, falling back to the general initial - * play time if there is no specific property interpolator. - */ - public long getInitialPlayTime(@PropType int propertyType) { - if (mPropInitialPlayTime != null) { - if (mPropInitialPlayTime.indexOfKey(propertyType) != -1) { - return mPropInitialPlayTime.get(propertyType); - } - return mPropInitialPlayTime.get(ALL, 0); - } - return 0; - } - - /** * Sets an animator listener for this animation. */ public AnimationProps setListener(Animator.AnimatorListener listener) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsEntrancePathInterpolator.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsEntrancePathInterpolator.java new file mode 100644 index 000000000000..e32da2d3f6be --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsEntrancePathInterpolator.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2017 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.recents.views; + +import android.view.animation.PathInterpolator; + +/** + * A helper interpolator to stagger the entrance animation in recents by offsetting the start time + */ +public class RecentsEntrancePathInterpolator extends PathInterpolator { + final float mStartOffsetFraction; + + /** + * Create an interpolator for a cubic Bezier curve with an offset play time. The end points + * <code>(0, 0)</code> and <code>(1, 1)</code> are assumed. + * + * @param controlX1 The x coordinate of the first control point of the cubic Bezier. + * @param controlY1 The y coordinate of the first control point of the cubic Bezier. + * @param controlX2 The x coordinate of the second control point of the cubic Bezier. + * @param controlY2 The y coordinate of the second control point of the cubic Bezier. + * @param startOffsetFraction The fraction from 0 to 1 to start the animation from + */ + public RecentsEntrancePathInterpolator(float controlX1, float controlY1, float controlX2, + float controlY2, float startOffsetFraction) { + super(controlX1, controlY1, controlX2, controlY2); + mStartOffsetFraction = startOffsetFraction; + } + + @Override + public float getInterpolation(float t) { + return super.getInterpolation(t + mStartOffsetFraction); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java index f1314aba1b46..0fc68e6a96ce 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java @@ -82,8 +82,6 @@ public class TaskStackAnimationHelper { private static final int ENTER_FROM_HOME_ALPHA_DURATION = 100; public static final int ENTER_FROM_HOME_TRANSLATION_DURATION = 300; - private static final Interpolator ENTER_FROM_HOME_TRANSLATION_INTERPOLATOR = - Interpolators.LINEAR_OUT_SLOW_IN; private static final Interpolator ENTER_FROM_HOME_ALPHA_INTERPOLATOR = Interpolators.LINEAR; public static final int EXIT_TO_HOME_TRANSLATION_DURATION = 200; @@ -260,17 +258,18 @@ public class TaskStackAnimationHelper { } else if (launchState.launchedFromHome) { // Animate the tasks up, but offset the animations to be relative to the front-most // task animation + final float startOffsetFraction = (float) (Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS, + taskIndexFromFront) * mEnterAndExitFromHomeTranslationOffset) / + ENTER_FROM_HOME_TRANSLATION_DURATION; AnimationProps taskAnimation = new AnimationProps() - .setInitialPlayTime(AnimationProps.BOUNDS, - Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS, taskIndexFromFront) * - mEnterAndExitFromHomeTranslationOffset) .setStartDelay(AnimationProps.ALPHA, Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS, taskIndexFromFront) * FRAME_OFFSET_MS) .setDuration(AnimationProps.BOUNDS, ENTER_FROM_HOME_TRANSLATION_DURATION) .setDuration(AnimationProps.ALPHA, ENTER_FROM_HOME_ALPHA_DURATION) .setInterpolator(AnimationProps.BOUNDS, - ENTER_FROM_HOME_TRANSLATION_INTERPOLATOR) + new RecentsEntrancePathInterpolator(0f, 0f, 0.2f, 1f, + startOffsetFraction)) .setInterpolator(AnimationProps.ALPHA, ENTER_FROM_HOME_ALPHA_INTERPOLATOR) .setListener(postAnimationTrigger.decrementOnAnimationEnd()); postAnimationTrigger.increment(); diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java index f750815d663f..4022718865e0 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java @@ -91,6 +91,9 @@ public class Divider extends SystemUI { } private void removeDivider() { + if (mView != null) { + mView.onDividerRemoved(); + } mWindowManager.remove(); } diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index 2bda1dfb5f88..09f459b0b07b 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -162,6 +162,9 @@ public class DividerView extends FrameLayout implements OnTouchListener, private DividerState mState; private final SurfaceFlingerVsyncChoreographer mSfChoreographer; + // The view is removed or in the process of been removed from the system. + private boolean mRemoved; + private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -323,6 +326,11 @@ public class DividerView extends FrameLayout implements OnTouchListener, EventBus.getDefault().unregister(this); } + void onDividerRemoved() { + mRemoved = true; + mHandler.removeMessages(MSG_RESIZE_STACK); + } + @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { if (mStableInsets.left != insets.getStableInsetLeft() @@ -927,6 +935,10 @@ public class DividerView extends FrameLayout implements OnTouchListener, } public void resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget) { + if (mRemoved) { + // This divider view has been removed so shouldn't have any additional influence. + return; + } calculateBoundsForPosition(position, mDockSide, mDockedRect); if (mDockedRect.equals(mLastResizeRect) && !mEntranceAnimationRunning) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 7f2f5f62add6..c445c0dd7787 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -369,15 +369,21 @@ public class ExpandableNotificationRow extends ActivatableNotificationView updateShelfIconColor(); } - private void updateShelfIconColor() { + @VisibleForTesting + void updateShelfIconColor() { StatusBarIconView expandedIcon = mEntry.expandedIcon; boolean isPreL = Boolean.TRUE.equals(expandedIcon.getTag(R.id.icon_is_pre_L)); boolean colorize = !isPreL || NotificationUtils.isGrayscale(expandedIcon, NotificationColorUtil.getInstance(mContext)); int color = StatusBarIconView.NO_COLOR; if (colorize) { - color = mEntry.getContrastedColor(mContext, mIsLowPriority && !isExpanded(), - getBackgroundColorWithoutTint()); + NotificationHeaderView header = getVisibleNotificationHeader(); + if (header != null) { + color = header.getOriginalIconColor(); + } else { + color = mEntry.getContrastedColor(mContext, mIsLowPriority && !isExpanded(), + getBackgroundColorWithoutTint()); + } } expandedIcon.setStaticDrawableColor(color); } @@ -1770,6 +1776,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView NotificationContentView showingLayout = getShowingLayout(); showingLayout.updateBackgroundColor(animated); mPrivateLayout.updateExpandButtons(isExpandable()); + updateShelfIconColor(); showingLayout.setDark(isDark(), false /* animate */, 0 /* delay */); mShowingPublicInitialized = true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java index 1844946e75b6..531437dff492 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java @@ -24,6 +24,8 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.Context; import android.graphics.drawable.Icon; +import android.os.AsyncTask; +import android.os.Bundle; import android.os.RemoteException; import android.os.SystemClock; import android.service.notification.NotificationListenerService; @@ -38,8 +40,11 @@ import android.widget.RemoteViews; import android.Manifest; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.messages.nano.SystemMessageProto; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.util.NotificationColorUtil; +import com.android.systemui.Dependency; +import com.android.systemui.ForegroundServiceController; import com.android.systemui.statusbar.notification.InflationException; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.StatusBar; @@ -339,6 +344,7 @@ public class NotificationData { mEntries.put(entry.notification.getKey(), entry); } mGroupManager.onEntryAdded(entry); + updateRankingAndSort(mRankingMap); } @@ -466,6 +472,10 @@ public class NotificationData { Collections.sort(mSortedAndFiltered, mRankingComparator); } + /** + * @param sbn + * @return true if this notification should NOT be shown right now + */ public boolean shouldFilterOut(StatusBarNotification sbn) { if (!(mEnvironment.isDeviceProvisioned() || showNotificationEvenIfUnprovisioned(sbn))) { @@ -487,6 +497,13 @@ public class NotificationData { && mGroupManager.isChildInGroupWithSummary(sbn)) { return true; } + + final ForegroundServiceController fsc = Dependency.get(ForegroundServiceController.class); + if (fsc.isDungeonNotification(sbn) && !fsc.isDungeonNeededForUser(sbn.getUserId())) { + // this is a foreground-service disclosure for a user that does not need to show one + return true; + } + return false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 14374b7ad787..3f1f82c6cd52 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -466,9 +466,8 @@ public class NotificationShelf extends ActivatableNotificationView implements } int shelfColor = icon.getStaticDrawableColor(); if (!noIcon && shelfColor != StatusBarIconView.NO_COLOR) { - int notificationColor - = row.getVisibleNotificationHeader().getOriginalNotificationColor(); - shelfColor = NotificationUtils.interpolateColors(notificationColor, shelfColor, + int iconColor = row.getVisibleNotificationHeader().getOriginalIconColor(); + shelfColor = NotificationUtils.interpolateColors(iconColor, shelfColor, iconState.iconAppearAmount); } iconState.iconColor = shelfColor; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index 6b30991164fa..fbad937d8481 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -45,7 +45,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; -import android.os.PowerManager; import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; @@ -202,8 +201,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_SCREEN_ON); getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); - PowerManager pm = getContext().getSystemService(PowerManager.class); - notifyNavigationBarScreenOn(pm.isScreenOn()); + notifyNavigationBarScreenOn(); } @Override @@ -378,8 +376,8 @@ public class NavigationBarFragment extends Fragment implements Callbacks { ((View) mNavigationBarView.getParent()).getLayoutParams()); } - private void notifyNavigationBarScreenOn(boolean screenOn) { - mNavigationBarView.notifyScreenOn(screenOn); + private void notifyNavigationBarScreenOn() { + mNavigationBarView.notifyScreenOn(); } private void prepareNavigationBarView() { @@ -604,10 +602,6 @@ public class NavigationBarFragment extends Fragment implements Callbacks { return mNavigationBarMode == MODE_SEMI_TRANSPARENT; } - public void onKeyguardOccludedChanged(boolean keyguardOccluded) { - mNavigationBarView.onKeyguardOccludedChanged(keyguardOccluded); - } - public void disableAnimationsDuringHide(long delay) { mNavigationBarView.setLayoutTransitionsEnabled(false); mNavigationBarView.postDelayed(() -> mNavigationBarView.setLayoutTransitionsEnabled(true), @@ -663,10 +657,9 @@ public class NavigationBarFragment extends Fragment implements Callbacks { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - if (Intent.ACTION_SCREEN_OFF.equals(action)) { - notifyNavigationBarScreenOn(false); - } else if (Intent.ACTION_SCREEN_ON.equals(action)) { - notifyNavigationBarScreenOn(true); + if (Intent.ACTION_SCREEN_OFF.equals(action) + || Intent.ACTION_SCREEN_ON.equals(action)) { + notifyNavigationBarScreenOn(); } } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 8d9d4610f1d4..cb3222d6d287 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -74,7 +74,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav View[] mRotatedViews = new View[4]; boolean mVertical; - boolean mScreenOn; private int mCurrentRotation = -1; boolean mShowMenu; @@ -367,8 +366,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav super.setLayoutDirection(layoutDirection); } - public void notifyScreenOn(boolean screenOn) { - mScreenOn = screenOn; + public void notifyScreenOn() { setDisabledFlags(mDisabledFlags, true); } @@ -634,9 +632,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav getHomeButton().setVertical(mVertical); } - public void onKeyguardOccludedChanged(boolean keyguardOccluded) { - } - private void updateTaskSwitchHelper() { if (mGestureHelper == null) return; boolean isRtl = (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 3326b3f581d7..a3c6e96f606b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -99,14 +99,11 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; -import android.os.SystemService; import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.os.Vibrator; import android.provider.Settings; -import android.service.dreams.DreamService; -import android.service.dreams.IDreamManager; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; @@ -120,7 +117,6 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; -import android.view.ContextThemeWrapper; import android.view.Display; import android.view.IWindowManager; import android.view.KeyEvent; @@ -161,6 +157,7 @@ import com.android.systemui.DejankUtils; import com.android.systemui.DemoMode; import com.android.systemui.Dependency; import com.android.systemui.EventLogTags; +import com.android.systemui.ForegroundServiceController; import com.android.systemui.Interpolators; import com.android.systemui.Prefs; import com.android.systemui.R; @@ -486,6 +483,7 @@ public class StatusBar extends SystemUI implements DemoMode, int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE; private final Rect mLastFullscreenStackBounds = new Rect(); private final Rect mLastDockedStackBounds = new Rect(); + private final Rect mTmpRect = new Rect(); // last value sent to window manager private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE; @@ -736,6 +734,7 @@ public class StatusBar extends SystemUI implements DemoMode, private boolean mClearAllEnabled; @Nullable private View mAmbientIndicationContainer; private ColorExtractor mColorExtractor; + private ForegroundServiceController mForegroundServiceController; private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) { final int N = array.size(); @@ -785,6 +784,9 @@ public class StatusBar extends SystemUI implements DemoMode, mColorExtractor.addOnColorsChangedListener(this); mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); + + mForegroundServiceController = Dependency.get(ForegroundServiceController.class); + mDisplay = mWindowManager.getDefaultDisplay(); updateDisplaySize(); @@ -1412,20 +1414,33 @@ public class StatusBar extends SystemUI implements DemoMode, int numChildren = mStackScroller.getChildCount(); final ArrayList<View> viewsToHide = new ArrayList<View>(numChildren); + final ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>(numChildren); for (int i = 0; i < numChildren; i++) { final View child = mStackScroller.getChildAt(i); if (child instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) child; + boolean parentVisible = false; + boolean hasClipBounds = child.getClipBounds(mTmpRect); if (mStackScroller.canChildBeDismissed(child)) { - if (child.getVisibility() == View.VISIBLE) { + viewsToRemove.add(row); + if (child.getVisibility() == View.VISIBLE + && (!hasClipBounds || mTmpRect.height() > 0)) { viewsToHide.add(child); + parentVisible = true; } + } else if (child.getVisibility() == View.VISIBLE + && (!hasClipBounds || mTmpRect.height() > 0)) { + parentVisible = true; } - ExpandableNotificationRow row = (ExpandableNotificationRow) child; List<ExpandableNotificationRow> children = row.getNotificationChildren(); - if (row.areChildrenExpanded() && children != null) { + if (children != null) { for (ExpandableNotificationRow childRow : children) { - if (mStackScroller.canChildBeDismissed(childRow)) { - if (childRow.getVisibility() == View.VISIBLE) { + viewsToRemove.add(childRow); + if (parentVisible && row.areChildrenExpanded() + && mStackScroller.canChildBeDismissed(childRow)) { + hasClipBounds = childRow.getClipBounds(mTmpRect); + if (childRow.getVisibility() == View.VISIBLE + && (!hasClipBounds || mTmpRect.height() > 0)) { viewsToHide.add(childRow); } } @@ -1433,7 +1448,7 @@ public class StatusBar extends SystemUI implements DemoMode, } } } - if (viewsToHide.isEmpty()) { + if (viewsToRemove.isEmpty()) { animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); return; } @@ -1442,6 +1457,13 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void run() { mStackScroller.setDismissAllInProgress(false); + for (ExpandableNotificationRow rowToRemove : viewsToRemove) { + if (mStackScroller.canChildBeDismissed(rowToRemove)) { + removeNotification(rowToRemove.getEntry().key, null); + } else { + rowToRemove.resetTranslation(); + } + } try { mBarService.onClearAllNotifications(mCurrentUserId); } catch (Exception ex) { } @@ -1618,6 +1640,10 @@ public class StatusBar extends SystemUI implements DemoMode, } } abortExistingInflation(key); + + mForegroundServiceController.addNotification(notification, + mNotificationData.getImportance(key)); + mPendingNotifications.put(key, shadeEntry); } @@ -1756,6 +1782,10 @@ public class StatusBar extends SystemUI implements DemoMode, return; } + if (entry != null) { + mForegroundServiceController.removeNotification(entry.notification); + } + if (entry != null && entry.row != null) { entry.row.setRemoved(); mStackScroller.cleanUpViewState(entry.row); @@ -3610,6 +3640,12 @@ public class StatusBar extends SystemUI implements DemoMode, // Do it after DismissAction has been processed to conserve the needed ordering. mHandler.post(this::runPostCollapseRunnables); } + } else if (isInLaunchTransition() && mNotificationPanel.isLaunchTransitionFinished()) { + + // We are not dismissing the shade, but the launch transition is already finished, + // so nobody will call readyForKeyguardDone anymore. Post it such that + // keyguardDonePending gets called first. + mHandler.post(mStatusBarKeyguardViewManager::readyForKeyguardDone); } return deferred; }, cancelAction, afterKeyguardGone); @@ -3859,10 +3895,6 @@ public class StatusBar extends SystemUI implements DemoMode, } } - public void onKeyguardOccludedChanged(boolean keyguardOccluded) { - mNavigationBar.onKeyguardOccludedChanged(keyguardOccluded); - } - // State logging private void logStateToEventlog() { @@ -6944,6 +6976,9 @@ public class StatusBar extends SystemUI implements DemoMode, entry.updateIcons(mContext, n); inflateViews(entry, mStackScroller); + mForegroundServiceController.updateNotification(notification, + mNotificationData.getImportance(key)); + boolean shouldPeek = shouldPeek(entry, notification); boolean alertAgain = alertAgain(entry, n); @@ -6961,6 +6996,7 @@ public class StatusBar extends SystemUI implements DemoMode, boolean isForCurrentUser = isNotificationForCurrentProfiles(notification); Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you"); } + setAreThereNotifications(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 5c3c43bffa70..7d69a349f01d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -279,9 +279,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } public void setOccluded(boolean occluded, boolean animate) { - if (occluded != mOccluded) { - mStatusBar.onKeyguardOccludedChanged(occluded); - } if (occluded && !mOccluded && mShowing) { if (mStatusBar.isInLaunchTransition()) { mOccluded = true; diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java index e86a34a56f17..f6d36e855964 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java @@ -60,7 +60,8 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna private final Context mContext; private final VolumeDialogControllerImpl mController; private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges( - ActivityInfo.CONFIG_FONT_SCALE); + ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE + | ActivityInfo.CONFIG_ASSETS_PATHS); private final Extension mExtension; private VolumeDialog mDialog; private VolumePolicy mVolumePolicy = new VolumePolicy( diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java new file mode 100644 index 000000000000..1f5255a0e869 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2017 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; + +import android.annotation.UserIdInt; +import android.app.Notification; +import android.app.NotificationManager; +import android.os.Bundle; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import com.android.internal.messages.nano.SystemMessageProto; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class ForegroundServiceControllerTest extends SysuiTestCase { + public static @UserIdInt int USERID_ONE = 10; // UserManagerService.MIN_USER_ID; + public static @UserIdInt int USERID_TWO = USERID_ONE + 1; + + private ForegroundServiceController fsc; + + @Before + public void setUp() throws Exception { + fsc = new ForegroundServiceControllerImpl(mContext); + } + + @Test + public void testNotificationCRUD() { + StatusBarNotification sbn_user1_app1_fg = makeMockFgSBN(USERID_ONE, "com.example.app1"); + StatusBarNotification sbn_user2_app2_fg = makeMockFgSBN(USERID_TWO, "com.example.app2"); + StatusBarNotification sbn_user1_app3_fg = makeMockFgSBN(USERID_ONE, "com.example.app3"); + StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, "com.example.app1", + 5000, "monkeys", Notification.FLAG_AUTO_CANCEL); + StatusBarNotification sbn_user2_app1 = makeMockSBN(USERID_TWO, "com.example.app1", + 5000, "monkeys", Notification.FLAG_AUTO_CANCEL); + + assertFalse(fsc.removeNotification(sbn_user1_app3_fg)); + assertFalse(fsc.removeNotification(sbn_user2_app2_fg)); + assertFalse(fsc.removeNotification(sbn_user1_app1_fg)); + assertFalse(fsc.removeNotification(sbn_user1_app1)); + assertFalse(fsc.removeNotification(sbn_user2_app1)); + + fsc.addNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT); + fsc.addNotification(sbn_user2_app2_fg, NotificationManager.IMPORTANCE_DEFAULT); + fsc.addNotification(sbn_user1_app3_fg, NotificationManager.IMPORTANCE_DEFAULT); + fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT); + fsc.addNotification(sbn_user2_app1, NotificationManager.IMPORTANCE_DEFAULT); + + // these are never added to the tracker + assertFalse(fsc.removeNotification(sbn_user1_app1)); + assertFalse(fsc.removeNotification(sbn_user2_app1)); + + fsc.updateNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT); + fsc.updateNotification(sbn_user2_app1, NotificationManager.IMPORTANCE_DEFAULT); + // should still not be there + assertFalse(fsc.removeNotification(sbn_user1_app1)); + assertFalse(fsc.removeNotification(sbn_user2_app1)); + + fsc.updateNotification(sbn_user2_app2_fg, NotificationManager.IMPORTANCE_DEFAULT); + fsc.updateNotification(sbn_user1_app3_fg, NotificationManager.IMPORTANCE_DEFAULT); + fsc.updateNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT); + + assertTrue(fsc.removeNotification(sbn_user1_app3_fg)); + assertFalse(fsc.removeNotification(sbn_user1_app3_fg)); + + assertTrue(fsc.removeNotification(sbn_user2_app2_fg)); + assertFalse(fsc.removeNotification(sbn_user2_app2_fg)); + + assertTrue(fsc.removeNotification(sbn_user1_app1_fg)); + assertFalse(fsc.removeNotification(sbn_user1_app1_fg)); + + assertFalse(fsc.removeNotification(sbn_user1_app1)); + assertFalse(fsc.removeNotification(sbn_user2_app1)); + } + + @Test + public void testDungeonPredicate() { + StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, "com.example.app1", + 5000, "monkeys", Notification.FLAG_AUTO_CANCEL); + StatusBarNotification sbn_user1_dungeon = makeMockSBN(USERID_ONE, "android", + SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICES, + null, Notification.FLAG_NO_CLEAR); + + assertTrue(fsc.isDungeonNotification(sbn_user1_dungeon)); + assertFalse(fsc.isDungeonNotification(sbn_user1_app1)); + } + + @Test + public void testDungeonCRUD() { + StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, "com.example.app1", + 5000, "monkeys", Notification.FLAG_AUTO_CANCEL); + StatusBarNotification sbn_user1_dungeon = makeMockSBN(USERID_ONE, "android", + SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICES, + null, Notification.FLAG_NO_CLEAR); + + fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT); + fsc.addNotification(sbn_user1_dungeon, NotificationManager.IMPORTANCE_DEFAULT); + + fsc.removeNotification(sbn_user1_dungeon); + assertFalse(fsc.removeNotification(sbn_user1_app1)); + } + + @Test + public void testNeedsDungeonAfterRemovingUnrelatedNotification() { + final String PKG1 = "com.example.app100"; + + StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, PKG1, + 5000, "monkeys", Notification.FLAG_AUTO_CANCEL); + StatusBarNotification sbn_user1_app1_fg = makeMockFgSBN(USERID_ONE, PKG1); + + // first add a normal notification + fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT); + // nothing required yet + assertFalse(fsc.isDungeonNeededForUser(USERID_ONE)); + // now the app starts a fg service + fsc.addNotification(makeMockDungeon(USERID_ONE, new String[]{ PKG1 }), + NotificationManager.IMPORTANCE_DEFAULT); + assertTrue(fsc.isDungeonNeededForUser(USERID_ONE)); // should be required! + // add the fg notification + fsc.addNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT); + assertFalse(fsc.isDungeonNeededForUser(USERID_ONE)); // app1 has got it covered + // remove the boring notification + fsc.removeNotification(sbn_user1_app1); + assertFalse(fsc.isDungeonNeededForUser(USERID_ONE)); // app1 has STILL got it covered + assertTrue(fsc.removeNotification(sbn_user1_app1_fg)); + assertTrue(fsc.isDungeonNeededForUser(USERID_ONE)); // should be required! + } + + @Test + public void testSimpleAddRemove() { + final String PKG1 = "com.example.app1"; + final String PKG2 = "com.example.app2"; + + StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, PKG1, + 5000, "monkeys", Notification.FLAG_AUTO_CANCEL); + fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT); + + // no services are "running" + fsc.addNotification(makeMockDungeon(USERID_ONE, null), + NotificationManager.IMPORTANCE_DEFAULT); + + assertFalse(fsc.isDungeonNeededForUser(USERID_ONE)); + assertFalse(fsc.isDungeonNeededForUser(USERID_TWO)); + + fsc.updateNotification(makeMockDungeon(USERID_ONE, new String[]{PKG1}), + NotificationManager.IMPORTANCE_DEFAULT); + assertTrue(fsc.isDungeonNeededForUser(USERID_ONE)); // should be required! + assertFalse(fsc.isDungeonNeededForUser(USERID_TWO)); + + // switch to different package + fsc.updateNotification(makeMockDungeon(USERID_ONE, new String[]{PKG2}), + NotificationManager.IMPORTANCE_DEFAULT); + assertTrue(fsc.isDungeonNeededForUser(USERID_ONE)); + assertFalse(fsc.isDungeonNeededForUser(USERID_TWO)); + + fsc.updateNotification(makeMockDungeon(USERID_TWO, new String[]{PKG1}), + NotificationManager.IMPORTANCE_DEFAULT); + assertTrue(fsc.isDungeonNeededForUser(USERID_ONE)); + assertTrue(fsc.isDungeonNeededForUser(USERID_TWO)); // finally user2 needs one too + + fsc.updateNotification(makeMockDungeon(USERID_ONE, new String[]{PKG2, PKG1}), + NotificationManager.IMPORTANCE_DEFAULT); + assertTrue(fsc.isDungeonNeededForUser(USERID_ONE)); + assertTrue(fsc.isDungeonNeededForUser(USERID_TWO)); + + fsc.removeNotification(makeMockDungeon(USERID_ONE, null /*unused*/)); + assertFalse(fsc.isDungeonNeededForUser(USERID_ONE)); + assertTrue(fsc.isDungeonNeededForUser(USERID_TWO)); + + fsc.removeNotification(makeMockDungeon(USERID_TWO, null /*unused*/)); + assertFalse(fsc.isDungeonNeededForUser(USERID_ONE)); + assertFalse(fsc.isDungeonNeededForUser(USERID_TWO)); + } + + @Test + public void testDungeonBasic() { + final String PKG1 = "com.example.app0"; + + StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, PKG1, + 5000, "monkeys", Notification.FLAG_AUTO_CANCEL); + StatusBarNotification sbn_user1_app1_fg = makeMockFgSBN(USERID_ONE, PKG1); + + fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT); // not fg + fsc.addNotification(makeMockDungeon(USERID_ONE, new String[]{ PKG1 }), + NotificationManager.IMPORTANCE_DEFAULT); + assertTrue(fsc.isDungeonNeededForUser(USERID_ONE)); // should be required! + fsc.addNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT); + assertFalse(fsc.isDungeonNeededForUser(USERID_ONE)); // app1 has got it covered + assertFalse(fsc.isDungeonNeededForUser(USERID_TWO)); + + // let's take out the other notification and see what happens. + + fsc.removeNotification(sbn_user1_app1); + assertFalse(fsc.isDungeonNeededForUser(USERID_ONE)); // still covered by sbn_user1_app1_fg + assertFalse(fsc.isDungeonNeededForUser(USERID_TWO)); + + // let's attempt to downgrade the notification from FLAG_FOREGROUND and see what we get + StatusBarNotification sbn_user1_app1_fg_sneaky = makeMockFgSBN(USERID_ONE, PKG1); + sbn_user1_app1_fg_sneaky.getNotification().flags = 0; + fsc.updateNotification(sbn_user1_app1_fg_sneaky, NotificationManager.IMPORTANCE_DEFAULT); + assertTrue(fsc.isDungeonNeededForUser(USERID_ONE)); // should be required! + assertFalse(fsc.isDungeonNeededForUser(USERID_TWO)); + + // ok, ok, we'll put it back + sbn_user1_app1_fg_sneaky.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE; + fsc.updateNotification(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT); + assertFalse(fsc.isDungeonNeededForUser(USERID_ONE)); + assertFalse(fsc.isDungeonNeededForUser(USERID_TWO)); + + assertTrue(fsc.removeNotification(sbn_user1_app1_fg_sneaky)); + assertTrue(fsc.isDungeonNeededForUser(USERID_ONE)); // should be required! + assertFalse(fsc.isDungeonNeededForUser(USERID_TWO)); + + // now let's test an upgrade + fsc.addNotification(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT); + assertTrue(fsc.isDungeonNeededForUser(USERID_ONE)); + assertFalse(fsc.isDungeonNeededForUser(USERID_TWO)); + sbn_user1_app1.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE; + fsc.updateNotification(sbn_user1_app1, + NotificationManager.IMPORTANCE_DEFAULT); // this is now a fg notification + + assertFalse(fsc.isDungeonNeededForUser(USERID_TWO)); + assertFalse(fsc.isDungeonNeededForUser(USERID_ONE)); + + // remove it, make sure we're out of compliance again + assertTrue(fsc.removeNotification(sbn_user1_app1)); // was fg, should return true + assertFalse(fsc.removeNotification(sbn_user1_app1)); + assertFalse(fsc.isDungeonNeededForUser(USERID_TWO)); + assertTrue(fsc.isDungeonNeededForUser(USERID_ONE)); + + // finally, let's turn off the service + fsc.addNotification(makeMockDungeon(USERID_ONE, null), + NotificationManager.IMPORTANCE_DEFAULT); + + assertFalse(fsc.isDungeonNeededForUser(USERID_ONE)); + assertFalse(fsc.isDungeonNeededForUser(USERID_TWO)); + } + + private StatusBarNotification makeMockSBN(int userid, String pkg, int id, String tag, + int flags) { + final Notification n = mock(Notification.class); + n.flags = flags; + return makeMockSBN(userid, pkg, id, tag, n); + } + private StatusBarNotification makeMockSBN(int userid, String pkg, int id, String tag, + Notification n) { + final StatusBarNotification sbn = mock(StatusBarNotification.class); + when(sbn.getNotification()).thenReturn(n); + when(sbn.getId()).thenReturn(id); + when(sbn.getPackageName()).thenReturn(pkg); + when(sbn.getTag()).thenReturn(null); + when(sbn.getUserId()).thenReturn(userid); + when(sbn.getUser()).thenReturn(new UserHandle(userid)); + when(sbn.getKey()).thenReturn("MOCK:"+userid+"|"+pkg+"|"+id+"|"+tag); + return sbn; + } + private StatusBarNotification makeMockFgSBN(int userid, String pkg) { + return makeMockSBN(userid, pkg, 1000, "foo", Notification.FLAG_FOREGROUND_SERVICE); + } + private StatusBarNotification makeMockDungeon(int userid, String[] pkgs) { + final Notification n = mock(Notification.class); + n.flags = Notification.FLAG_ONGOING_EVENT; + final Bundle extras = new Bundle(); + if (pkgs != null) extras.putStringArray(Notification.EXTRA_FOREGROUND_APPS, pkgs); + n.extras = extras; + final StatusBarNotification sbn = makeMockSBN(userid, "android", + SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICES, + null, n); + sbn.getNotification().extras = extras; + return sbn; + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java index 628630182ad1..99b664ad9580 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.content.Context; @@ -88,5 +89,11 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { verify(mockContainer).reInflateViews(any(), any()); } - + @Test + public void testIconColorShouldBeUpdatedWhenSensitive() throws Exception { + ExpandableNotificationRow row = spy(mNotificationTestHelper.createRow()); + row.setSensitive(true, true); + row.setHideSensitive(true, false, 0, 0); + verify(row).updateShelfIconColor(); + } } diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index 57885fa06715..e1070561a7df 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -4079,6 +4079,12 @@ message MetricsEvent { // OS: O DR BLUETOOTH_DEVICE_DETAILS = 1009; + // OPEN: Settings > credential pages - prompt for key guard configuration confirmation + CONFIGURE_KEYGUARD_DIALOG = 1010; + + // Open: Settings > Search > No Result View + SETTINGS_SEARCH_NO_RESULT = 1011; + // Add new aosp constants above this line. // END OF AOSP CONSTANTS } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 980a7d41200f..073d7b20ed64 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -774,20 +774,29 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState break; } - final AutofillValue currentValue = viewState.getCurrentValue(); - if (currentValue == null || currentValue.isEmpty()) { - if (sDebug) { - Slog.d(TAG, "showSaveLocked(): empty value for required " + id ); + AutofillValue value = viewState.getCurrentValue(); + if (value == null || value.isEmpty()) { + final AutofillValue initialValue = getValueFromContexts(id); + if (initialValue != null) { + if (sDebug) { + Slog.d(TAG, "Value of required field " + id + " didn't change; " + + "using initial value (" + initialValue + ") instead"); + } + value = initialValue; + } else { + if (sDebug) { + Slog.d(TAG, "showSaveLocked(): empty value for required " + id ); + } + allRequiredAreNotEmpty = false; + break; } - allRequiredAreNotEmpty = false; - break; } final AutofillValue filledValue = viewState.getAutofilledValue(); - if (!currentValue.equals(filledValue)) { + if (!value.equals(filledValue)) { if (sDebug) { Slog.d(TAG, "showSaveLocked(): found a change on required " + id + ": " - + filledValue + " => " + currentValue); + + filledValue + " => " + value); } atLeastOneChanged = true; } @@ -845,6 +854,31 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** + * Gets the latest non-empty value for the given id in the autofill contexts. + */ + @Nullable + private AutofillValue getValueFromContexts(AutofillId id) { + AutofillValue value = null; + final int numContexts = mContexts.size(); + for (int i = 0; i < numContexts; i++) { + final FillContext context = mContexts.get(i); + // TODO: create a function that gets just one node so it doesn't create an array + // unnecessarily + final ViewNode[] nodes = context.findViewNodesByAutofillIds(id); + if (nodes != null) { + AutofillValue candidate = nodes[0].getAutofillValue(); + if (sDebug) { + Slog.d(TAG, "getValueFromContexts(" + id + ") at " + i + ": " + candidate); + } + if (candidate != null && !candidate.isEmpty()) { + value = candidate; + } + } + } + return value; + } + + /** * Calls service when user requested save. */ void callSaveLocked() { @@ -1009,7 +1043,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState || action == ACTION_VIEW_ENTERED) { if (sVerbose) Slog.v(TAG, "Creating viewState for " + id + " on " + action); boolean isIgnored = isIgnoredLocked(id); - viewState = new ViewState(this, id, value, this, + viewState = new ViewState(this, id, this, isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL); mViewStates.put(id, viewState); if (isIgnored) { @@ -1307,7 +1341,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (viewState != null) { viewState.setState(state); } else { - viewState = new ViewState(this, id, null, this, state); + viewState = new ViewState(this, id, this, state); if (sVerbose) { Slog.v(TAG, "Adding autofillable view with id " + id + " and state " + state); } @@ -1370,10 +1404,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } + @Override + public String toString() { + return "Session: [id=" + id + ", pkg=" + mPackageName + "]"; + } + void dumpLocked(String prefix, PrintWriter pw) { final String prefix2 = prefix + " "; pw.print(prefix); pw.print("id: "); pw.println(id); pw.print(prefix); pw.print("uid: "); pw.println(uid); + pw.print(prefix); pw.print("mPackagename: "); pw.println(mPackageName); pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken); pw.print(prefix); pw.print("mResponses: "); if (mResponses == null) { diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java index cd8f4a59526f..51659bb8c8b3 100644 --- a/services/autofill/java/com/android/server/autofill/ViewState.java +++ b/services/autofill/java/com/android/server/autofill/ViewState.java @@ -74,16 +74,14 @@ final class ViewState { private final Session mSession; private FillResponse mResponse; - private AutofillValue mInitialValue; private AutofillValue mCurrentValue; private AutofillValue mAutofilledValue; private Rect mVirtualBounds; private int mState; - ViewState(Session session, AutofillId id, AutofillValue value, Listener listener, int state) { + ViewState(Session session, AutofillId id, Listener listener, int state) { mSession = session; this.id = id; - mInitialValue = value; mListener = listener; mState = state; } @@ -118,11 +116,6 @@ final class ViewState { } @Nullable - AutofillValue getInitialValue() { - return mInitialValue; - } - - @Nullable FillResponse getResponse() { return mResponse; } @@ -189,8 +182,8 @@ final class ViewState { @Override public String toString() { - return "ViewState: [id=" + id + ", initialValue=" + mInitialValue - + ", currentValue=" + mCurrentValue + ", autofilledValue=" + mAutofilledValue + return "ViewState: [id=" + id + ", currentValue=" + mCurrentValue + + ", autofilledValue=" + mAutofilledValue + ", bounds=" + mVirtualBounds + ", state=" + getStateAsString() + "]"; } @@ -207,7 +200,6 @@ final class ViewState { pw.println(mResponse.getRequestId()); } } - pw.print(prefix); pw.print("initialValue:" ); pw.println(mInitialValue); pw.print(prefix); pw.print("currentValue:" ); pw.println(mCurrentValue); pw.print(prefix); pw.print("autofilledValue:" ); pw.println(mAutofilledValue); pw.print(prefix); pw.print("virtualBounds:" ); pw.println(mVirtualBounds); diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java index c3d550549a3f..8ffe8f547eac 100644 --- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java +++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java @@ -332,6 +332,10 @@ public final class AutoFillUI { @android.annotation.UiThread private void hideSaveUiUiThread(@Nullable AutoFillUiCallback callback) { + if (sVerbose) { + Slog.v(TAG, "hideSaveUiUiThread(): mSaveUi=" + mSaveUi + ", callback=" + callback + + ", mCallback=" + mCallback); + } if (mSaveUi != null && (callback == null || callback == mCallback)) { mSaveUi.destroy(); mSaveUi = null; diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java index d566d3d608b4..e9c98e94c860 100644 --- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java @@ -398,6 +398,12 @@ final class FillUi { } catch (WindowManager.BadTokenException e) { if (sDebug) Slog.d(TAG, "Filed with with token " + params.token + " gone."); mCallback.onDestroy(); + } catch (IllegalStateException e) { + // WM throws an ISE if mContentView was added twice; this should never happen - + // since show() and hide() are always called in the UIThread - but when it does, + // it should not crash the system. + Slog.e(TAG, "Exception showing window " + params, e); + mCallback.onDestroy(); } } @@ -405,10 +411,18 @@ final class FillUi { * Hides the window. */ void hide() { - if (mShowing) { - mContentView.setOnTouchListener(null); - mWm.removeView(mContentView); - mShowing = false; + try { + if (mShowing) { + mContentView.setOnTouchListener(null); + mWm.removeView(mContentView); + mShowing = false; + } + } catch (IllegalStateException e) { + // WM might thrown an ISE when removing the mContentView; this should never + // happen - since show() and hide() are always called in the UIThread - but if it + // does, it should not crash the system. + Slog.e(TAG, "Exception hiding window ", e); + mCallback.onDestroy(); } } @@ -489,7 +503,7 @@ final class FillUi { final String value = item.getValue(); // No value, i.e. null, matches any filter if (value == null - || value.toLowerCase().contains(constraintLowerCase)) { + || value.toLowerCase().startsWith(constraintLowerCase)) { filteredItems.add(item); } } diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java index d25ffced7797..491af91cbc36 100644 --- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java @@ -17,6 +17,7 @@ package com.android.server.autofill.ui; import static com.android.server.autofill.Helper.sDebug; +import static com.android.server.autofill.Helper.sVerbose; import android.annotation.NonNull; import android.app.Dialog; @@ -63,7 +64,7 @@ final class SaveUi { @Override public void onSave() { - if (sDebug) Slog.d(TAG, "onSave(): " + mDone); + if (sDebug) Slog.d(TAG, "OneTimeListener.onSave(): " + mDone); if (mDone) { return; } @@ -73,7 +74,7 @@ final class SaveUi { @Override public void onCancel(IntentSender listener) { - if (sDebug) Slog.d(TAG, "onCancel(): " + mDone); + if (sDebug) Slog.d(TAG, "OneTimeListener.onCancel(): " + mDone); if (mDone) { return; } @@ -83,7 +84,7 @@ final class SaveUi { @Override public void onDestroy() { - if (sDebug) Slog.d(TAG, "onDestroy(): " + mDone); + if (sDebug) Slog.d(TAG, "OneTimeListener.onDestroy(): " + mDone); if (mDone) { return; } @@ -158,9 +159,8 @@ final class SaveUi { subTitleView.setVisibility(View.VISIBLE); } - Slog.i(TAG, "Showing save dialog: " + mTitle); if (sDebug) { - Slog.d(TAG, "SubTitle: " + mSubTitle); + Slog.d(TAG, "on constructor: title=" + mTitle + ", subTitle=" + mSubTitle); } final TextView noButton = view.findViewById(R.id.autofill_save_no); @@ -169,15 +169,15 @@ final class SaveUi { } else { noButton.setText(R.string.autofill_save_no); } - noButton.setOnClickListener((v) -> mListener.onCancel( - info.getNegativeActionListener())); + View.OnClickListener cancelListener = + (v) -> mListener.onCancel(info.getNegativeActionListener()); + noButton.setOnClickListener(cancelListener); final View yesButton = view.findViewById(R.id.autofill_save_yes); yesButton.setOnClickListener((v) -> mListener.onSave()); final View closeButton = view.findViewById(R.id.autofill_save_close); - closeButton.setOnClickListener((v) -> mListener.onCancel( - info.getNegativeActionListener())); + closeButton.setOnClickListener(cancelListener); mDialog = new Dialog(context, R.style.Theme_DeviceDefault_Light_Panel); mDialog.setContentView(view); @@ -195,13 +195,16 @@ final class SaveUi { params.width = WindowManager.LayoutParams.MATCH_PARENT; params.accessibilityTitle = context.getString(R.string.autofill_save_accessibility_title); + Slog.i(TAG, "Showing save dialog: " + mTitle); mDialog.show(); } void destroy() { + if (sDebug) Slog.d(TAG, "destroy()"); throwIfDestroyed(); mListener.onDestroy(); mHandler.removeCallbacksAndMessages(mListener); + if (sVerbose) Slog.v(TAG, "destroy(): dismissing dialog"); mDialog.dismiss(); mDestroyed = true; } @@ -212,6 +215,11 @@ final class SaveUi { } } + @Override + public String toString() { + return mTitle == null ? "NO TITLE" : mTitle.toString(); + } + void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("title: "); pw.println(mTitle); pw.print(prefix); pw.print("subtitle: "); pw.println(mSubTitle); diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java index 47f157da8790..b408da85425d 100644 --- a/services/core/java/com/android/server/GestureLauncherService.java +++ b/services/core/java/com/android/server/GestureLauncherService.java @@ -320,7 +320,8 @@ public class GestureLauncherService extends SystemService { public static boolean isCameraLiftTriggerSettingEnabled(Context context, int userId) { return isCameraLiftTriggerEnabled(context.getResources()) && (Settings.Secure.getIntForUser(context.getContentResolver(), - Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED, 0, userId) != 0); + Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED, + Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED_DEFAULT, userId) != 0); } /** diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 4a78a7bc4b51..e4a2c2f20762 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -23,8 +23,10 @@ import android.accounts.AccountAndUser; import android.accounts.AccountAuthenticatorResponse; import android.accounts.AccountManager; import android.accounts.AccountManagerInternal; +import android.accounts.AccountManagerResponse; import android.accounts.AuthenticatorDescription; import android.accounts.CantAddAccountActivity; +import android.accounts.ChooseAccountActivity; import android.accounts.GrantCredentialsPermissionActivity; import android.accounts.IAccountAuthenticator; import android.accounts.IAccountAuthenticatorResponse; @@ -71,6 +73,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Parcel; +import android.os.Parcelable; import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteException; @@ -4043,6 +4046,7 @@ public class AccountManagerService private volatile int mCurrentAccount = 0; private final int mCallingUid; private final String mPackageName; + private final boolean mIncludeManagedNotVisible; public GetAccountsByTypeAndFeatureSession( UserAccounts accounts, @@ -4050,19 +4054,21 @@ public class AccountManagerService String type, String[] features, int callingUid, - String packageName) { + String packageName, + boolean includeManagedNotVisible) { super(accounts, response, type, false /* expectActivityLaunch */, true /* stripAuthTokenFromResult */, null /* accountName */, false /* authDetailsRequired */); mCallingUid = callingUid; mFeatures = features; mPackageName = packageName; + mIncludeManagedNotVisible = includeManagedNotVisible; } @Override public void run() throws RemoteException { mAccountsOfType = getAccountsFromCache(mAccounts, mAccountType, - mCallingUid, mPackageName, false /* include managed not visible*/); + mCallingUid, mPackageName, mIncludeManagedNotVisible); // check whether each account matches the requested features mAccountsWithFeatures = new ArrayList<>(mAccountsOfType.length); mCurrentAccount = 0; @@ -4423,10 +4429,120 @@ public class AccountManagerService && (type != null && !isAccountManagedByCaller(type, callingUid, userId))) { return EMPTY_ACCOUNT_ARRAY; } + if (!UserHandle.isSameApp(callingUid, Process.SYSTEM_UID) && type == null) { + return getAccountsAsUserForPackage(type, userId, + packageName, packageUid, opPackageName, false /* includeUserManagedNotVisible */); + } return getAccountsAsUserForPackage(type, userId, packageName, packageUid, opPackageName, true /* includeUserManagedNotVisible */); } + private boolean needToStartChooseAccountActivity(Account[] accounts, String callingPackage) { + if (accounts.length < 1) return false; + if (accounts.length > 1) return true; + Account account = accounts[0]; + UserAccounts userAccounts = getUserAccounts(UserHandle.getCallingUserId()); + int visibility = resolveAccountVisibility(account, callingPackage, userAccounts); + if (visibility == AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE) return true; + return false; + } + + private void startChooseAccountActivityWithAccounts( + IAccountManagerResponse response, Account[] accounts) { + Intent intent = new Intent(mContext, ChooseAccountActivity.class); + intent.putExtra(AccountManager.KEY_ACCOUNTS, accounts); + intent.putExtra(AccountManager.KEY_ACCOUNT_MANAGER_RESPONSE, + new AccountManagerResponse(response)); + + mContext.startActivityAsUser(intent, UserHandle.of(UserHandle.getCallingUserId())); + } + + private void handleGetAccountsResult( + IAccountManagerResponse response, + Account[] accounts, + String callingPackage) { + + if (needToStartChooseAccountActivity(accounts, callingPackage)) { + startChooseAccountActivityWithAccounts(response, accounts); + return; + } + if (accounts.length == 1) { + Bundle bundle = new Bundle(); + bundle.putString(AccountManager.KEY_ACCOUNT_NAME, accounts[0].name); + bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, accounts[0].type); + onResult(response, bundle); + return; + } + // No qualified account exists, return an empty Bundle. + onResult(response, new Bundle()); + } + + @Override + public void getAccountByTypeAndFeatures( + IAccountManagerResponse response, + String accountType, + String[] features, + String opPackageName) { + + int callingUid = Binder.getCallingUid(); + mAppOpsManager.checkPackage(callingUid, opPackageName); + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "getAccount: accountType " + accountType + + ", response " + response + + ", features " + Arrays.toString(features) + + ", caller's uid " + callingUid + + ", pid " + Binder.getCallingPid()); + } + if (response == null) throw new IllegalArgumentException("response is null"); + if (accountType == null) throw new IllegalArgumentException("accountType is null"); + + int userId = UserHandle.getCallingUserId(); + + long identityToken = clearCallingIdentity(); + try { + UserAccounts userAccounts = getUserAccounts(userId); + if (ArrayUtils.isEmpty(features)) { + Account[] accountsWithManagedNotVisible = getAccountsFromCache( + userAccounts, accountType, callingUid, opPackageName, + true /* include managed not visible */); + handleGetAccountsResult( + response, accountsWithManagedNotVisible, opPackageName); + return; + } + + IAccountManagerResponse retrieveAccountsResponse = + new IAccountManagerResponse.Stub() { + @Override + public void onResult(Bundle value) throws RemoteException { + Parcelable[] parcelables = value.getParcelableArray( + AccountManager.KEY_ACCOUNTS); + Account[] accounts = new Account[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + accounts[i] = (Account) parcelables[i]; + } + handleGetAccountsResult( + response, accounts, opPackageName); + } + + @Override + public void onError(int errorCode, String errorMessage) + throws RemoteException { + // Will not be called in this case. + } + }; + new GetAccountsByTypeAndFeatureSession( + userAccounts, + retrieveAccountsResponse, + accountType, + features, + callingUid, + opPackageName, + true /* include managed not visible */).bind(); + } finally { + restoreCallingIdentity(identityToken); + } + } + @Override public void getAccountsByFeatures( IAccountManagerResponse response, @@ -4459,6 +4575,7 @@ public class AccountManagerService } return; } + long identityToken = clearCallingIdentity(); try { UserAccounts userAccounts = getUserAccounts(userId); @@ -4476,7 +4593,8 @@ public class AccountManagerService type, features, callingUid, - opPackageName).bind(); + opPackageName, + false /* include managed not visible */).bind(); } finally { restoreCallingIdentity(identityToken); } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 2680b425ff3c..bad7091429a2 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -162,7 +162,7 @@ public final class ActiveServices { /** * Information about an app that is currently running one or more foreground services. - * (This mapps directly to the running apps we show in the notification.) + * (This maps directly to the running apps we show in the notification.) */ static final class ActiveForegroundApp { String mPackageName; @@ -813,6 +813,7 @@ public final class ActiveServices { String title; String msg; String[] pkgs; + long oldestStartTime = System.currentTimeMillis(); // now if (active.size() == 1) { intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.setData(Uri.fromParts("package", active.get(0).mPackageName, null)); @@ -820,11 +821,13 @@ public final class ActiveServices { R.string.foreground_service_app_in_background, active.get(0).mLabel); msg = context.getString(R.string.foreground_service_tap_for_details); pkgs = new String[] { active.get(0).mPackageName }; + oldestStartTime = active.get(0).mStartTime; } else { intent = new Intent(Settings.ACTION_FOREGROUND_SERVICES_SETTINGS); pkgs = new String[active.size()]; for (int i = 0; i < active.size(); i++) { pkgs[i] = active.get(i).mPackageName; + oldestStartTime = Math.min(oldestStartTime, active.get(i).mStartTime); } intent.putExtra("packages", pkgs); title = context.getString( @@ -841,9 +844,10 @@ public final class ActiveServices { new Notification.Builder(context, SystemNotificationChannels.FOREGROUND_SERVICE) .addExtras(notificationBundle) - .setSmallIcon(R.drawable.ic_check_circle_24px) + .setSmallIcon(R.drawable.stat_sys_vitals) .setOngoing(true) - .setShowWhen(false) + .setShowWhen(oldestStartTime > 0) + .setWhen(oldestStartTime) .setColor(context.getColor( com.android.internal.R.color.system_notification_accent_color)) .setContentTitle(title) diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index c20221bfe4aa..06ab75a8fb40 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -188,6 +188,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub } private native int getPlatformLowPowerStats(ByteBuffer outBuffer); + private native int getSubsystemLowPowerStats(ByteBuffer outBuffer); private CharsetDecoder mDecoderStat = StandardCharsets.UTF_8 .newDecoder() .onMalformedInput(CodingErrorAction.REPLACE) @@ -219,6 +220,28 @@ public final class BatteryStatsService extends IBatteryStats.Stub } } + @Override + public String getSubsystemLowPowerStats() { + Slog.d(TAG, "begin getSubsystemLowPowerStats"); + try { + mUtf8BufferStat.clear(); + mUtf16BufferStat.clear(); + mDecoderStat.reset(); + int bytesWritten = getSubsystemLowPowerStats(mUtf8BufferStat); + if (bytesWritten < 0) { + return null; + } else if (bytesWritten == 0) { + return "Empty"; + } + mUtf8BufferStat.limit(bytesWritten); + mDecoderStat.decode(mUtf8BufferStat, mUtf16BufferStat, true); + mUtf16BufferStat.flip(); + return mUtf16BufferStat.toString(); + } finally { + Slog.d(TAG, "end getSubsystemLowPowerStats"); + } + } + BatteryStatsService(File systemDir, Handler handler) { // Our handler here will be accessing the disk, use a different thread than // what the ActivityManagerService gave us (no I/O on that one!). diff --git a/services/core/java/com/android/server/am/TaskChangeNotificationController.java b/services/core/java/com/android/server/am/TaskChangeNotificationController.java index ea9ff592d6e1..82971696d670 100644 --- a/services/core/java/com/android/server/am/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/am/TaskChangeNotificationController.java @@ -16,7 +16,6 @@ package com.android.server.am; -import android.app.ActivityManager; import android.app.ActivityManager.TaskSnapshot; import android.app.ITaskStackListener; import android.app.ActivityManager.TaskDescription; @@ -31,27 +30,27 @@ import android.os.RemoteException; import java.util.ArrayList; class TaskChangeNotificationController { - static final int LOG_STACK_STATE_MSG = 1; - static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG = 2; - static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 3; - static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 4; - static final int NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG = 5; - static final int NOTIFY_FORCED_RESIZABLE_MSG = 6; - static final int NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG = 7; - static final int NOTIFY_TASK_ADDED_LISTENERS_MSG = 8; - static final int NOTIFY_TASK_REMOVED_LISTENERS_MSG = 9; - static final int NOTIFY_TASK_MOVED_TO_FRONT_LISTENERS_MSG = 10; - static final int NOTIFY_TASK_DESCRIPTION_CHANGED_LISTENERS_MSG = 11; - static final int NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS = 12; - static final int NOTIFY_TASK_REMOVAL_STARTED_LISTENERS = 13; - static final int NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG = 14; - static final int NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG = 15; - static final int NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG = 16; - static final int NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG = 17; - static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG = 18; + private static final int LOG_STACK_STATE_MSG = 1; + private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG = 2; + private static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 3; + private static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 4; + private static final int NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG = 5; + private static final int NOTIFY_FORCED_RESIZABLE_MSG = 6; + private static final int NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG = 7; + private static final int NOTIFY_TASK_ADDED_LISTENERS_MSG = 8; + private static final int NOTIFY_TASK_REMOVED_LISTENERS_MSG = 9; + private static final int NOTIFY_TASK_MOVED_TO_FRONT_LISTENERS_MSG = 10; + private static final int NOTIFY_TASK_DESCRIPTION_CHANGED_LISTENERS_MSG = 11; + private static final int NOTIFY_ACTIVITY_REQUESTED_ORIENTATION_CHANGED_LISTENERS = 12; + private static final int NOTIFY_TASK_REMOVAL_STARTED_LISTENERS = 13; + private static final int NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG = 14; + private static final int NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG = 15; + private static final int NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG = 16; + private static final int NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG = 17; + private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG = 18; // Delay in notifying task stack change listeners (in millis) - static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100; + private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100; private final ActivityManagerService mService; private final ActivityStackSupervisor mStackSupervisor; @@ -242,7 +241,7 @@ class TaskChangeNotificationController { } } - void forAllRemoteListeners(TaskStackConsumer callback, Message message) { + private void forAllRemoteListeners(TaskStackConsumer callback, Message message) { synchronized (mService) { for (int i = mRemoteTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) { try { @@ -256,7 +255,7 @@ class TaskChangeNotificationController { } } - void forAllLocalListeners(TaskStackConsumer callback, Message message) { + private void forAllLocalListeners(TaskStackConsumer callback, Message message) { synchronized (mService) { for (int i = mLocalTaskStackListeners.size() - 1; i >= 0; i--) { try { @@ -329,8 +328,9 @@ class TaskChangeNotificationController { void notifyActivityDismissingDockedStack() { mHandler.removeMessages(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG); - final Message message = mHandler.obtainMessage(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG); - forAllLocalListeners(mNotifyActivityDismissingDockedStack, message); + final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG); + forAllLocalListeners(mNotifyActivityDismissingDockedStack, msg); + msg.sendToTarget(); } void notifyActivityForcedResizable(int taskId, int reason, String packageName) { diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java index c64e70516abc..cb50e9fdbfed 100644 --- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java +++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java @@ -20,10 +20,15 @@ import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; import android.content.ContentResolver; import android.net.LinkProperties; +import android.net.RouteInfo; import android.net.util.SharedLog; import android.os.Handler; import android.provider.Settings; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.util.ArrayList; + /** * A class to encapsulate the business logic of programming the tethering * hardware offload interface. @@ -92,8 +97,12 @@ public class OffloadController { public void setUpstreamLinkProperties(LinkProperties lp) { if (!started()) return; - // TODO: setUpstreamParameters(). - mUpstreamLinkProperties = lp; + mUpstreamLinkProperties = (lp != null) ? new LinkProperties(lp) : null; + // TODO: examine return code and decide what to do if programming + // upstream parameters fails (probably just wait for a subsequent + // onOffloadEvent() callback to tell us offload is available again and + // then reapply all state). + pushUpstreamParameters(); } // TODO: public void addDownStream(...) @@ -106,4 +115,40 @@ public class OffloadController { private boolean started() { return mConfigInitialized && mControlInitialized; } + + private boolean pushUpstreamParameters() { + if (mUpstreamLinkProperties == null) { + return mHwInterface.setUpstreamParameters(null, null, null, null); + } + + // A stacked interface cannot be an upstream for hardware offload. + // Consequently, we examine only the primary interface name, look at + // getAddresses() rather than getAllAddresses(), and check getRoutes() + // rather than getAllRoutes(). + final String iface = mUpstreamLinkProperties.getInterfaceName(); + final ArrayList<String> v6gateways = new ArrayList<>(); + String v4addr = null; + String v4gateway = null; + + for (InetAddress ip : mUpstreamLinkProperties.getAddresses()) { + if (ip instanceof Inet4Address) { + v4addr = ip.getHostAddress(); + break; + } + } + + // Find the gateway addresses of all default routes of either address family. + for (RouteInfo ri : mUpstreamLinkProperties.getRoutes()) { + if (!ri.hasGateway()) continue; + + final String gateway = ri.getGateway().getHostAddress(); + if (ri.isIPv4Default()) { + v4gateway = gateway; + } else if (ri.isIPv6Default()) { + v6gateways.add(gateway); + } + } + + return mHwInterface.setUpstreamParameters(iface, v4addr, v4gateway, v6gateways); + } } diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java index 0429ab3dca92..3ecf0d1d0c71 100644 --- a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java +++ b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java @@ -23,6 +23,8 @@ import android.os.Handler; import android.os.RemoteException; import android.net.util.SharedLog; +import java.util.ArrayList; + /** * Capture tethering dependencies, for injection. @@ -103,6 +105,25 @@ public class OffloadHardwareInterface { mControlCallback = null; } + public boolean setUpstreamParameters( + String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) { + final CbResults results = new CbResults(); + try { + mOffloadControl.setUpstreamParameters( + iface, v4addr, v4gateway, v6gws, + (boolean success, String errMsg) -> { + results.success = success; + results.errMsg = errMsg; + }); + } catch (RemoteException e) { + mLog.e("failed to setUpstreamParameters: " + e); + return false; + } + + if (!results.success) mLog.e("setUpstreamParameters failed: " + results.errMsg); + return results.success; + } + private static class TetheringOffloadCallback extends ITetheringOffloadCallback.Stub { public final Handler handler; public final ControlCallback controlCb; diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 9dc317ad38ac..e82724db27f7 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -104,6 +104,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Trigger proximity if distance is less than 5 cm. private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f; + // State machine constants for tracking initial brightness ramp skipping when enabled. + private static final int RAMP_STATE_SKIP_NONE = 0; + private static final int RAMP_STATE_SKIP_INITIAL = 1; + private static final int RAMP_STATE_SKIP_AUTOBRIGHT = 2; + private static final int REPORTED_TO_POLICY_SCREEN_OFF = 0; private static final int REPORTED_TO_POLICY_SCREEN_TURNING_ON = 1; private static final int REPORTED_TO_POLICY_SCREEN_ON = 2; @@ -239,6 +244,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Screen state we reported to policy. Must be one of REPORTED_TO_POLICY_SCREEN_* fields. private int mReportedScreenStateToPolicy; + // If the last recorded screen state was dozing or not. + private boolean mDozing; + // Remembers whether certain kinds of brightness adjustments // were recently applied so that we can decide how to transition. private boolean mAppliedAutoBrightness; @@ -249,6 +257,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private final int mBrightnessRampRateFast; private final int mBrightnessRampRateSlow; + // Whether or not to skip the initial brightness ramps into STATE_ON. + private final boolean mSkipScreenOnBrightnessRamp; + + // A record of state for skipping brightness ramps. + private int mSkipRampState = RAMP_STATE_SKIP_NONE; + + // The first autobrightness value set when entering RAMP_STATE_SKIP_INITIAL. + private int mInitialAutoBrightness; + // The controller for the automatic brightness level. private AutomaticBrightnessController mAutomaticBrightnessController; @@ -312,6 +329,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call com.android.internal.R.integer.config_brightness_ramp_rate_fast); mBrightnessRampRateSlow = resources.getInteger( com.android.internal.R.integer.config_brightness_ramp_rate_slow); + mSkipScreenOnBrightnessRamp = resources.getBoolean( + com.android.internal.R.bool.config_skipScreenOnBrightnessRamp); int lightSensorRate = resources.getInteger( com.android.internal.R.integer.config_autoBrightnessLightSensorRate); @@ -731,8 +750,29 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Animate the screen brightness when the screen is on or dozing. // Skip the animation when the screen is off or suspended or transition to/from VR. if (!mPendingScreenOff) { + if (mSkipScreenOnBrightnessRamp) { + + if (state == Display.STATE_ON) { + if (mSkipRampState == RAMP_STATE_SKIP_NONE && mDozing) { + mInitialAutoBrightness = brightness; + mSkipRampState = RAMP_STATE_SKIP_INITIAL; + } else if (mSkipRampState == RAMP_STATE_SKIP_INITIAL + && mUseSoftwareAutoBrightnessConfig + && brightness != mInitialAutoBrightness) { + mSkipRampState = RAMP_STATE_SKIP_AUTOBRIGHT; + } else if (mSkipRampState == RAMP_STATE_SKIP_AUTOBRIGHT) { + mSkipRampState = RAMP_STATE_SKIP_NONE; + } + } else { + mSkipRampState = RAMP_STATE_SKIP_NONE; + } + } + boolean wasOrWillBeInVr = (state == Display.STATE_VR || oldState == Display.STATE_VR); - if ((state == Display.STATE_ON || state == Display.STATE_DOZE) && !wasOrWillBeInVr) { + if ((state == Display.STATE_ON + && mSkipRampState == RAMP_STATE_SKIP_NONE + || state == Display.STATE_DOZE) + && !wasOrWillBeInVr) { animateScreenBrightness(brightness, slowChange ? mBrightnessRampRateSlow : mBrightnessRampRateFast); } else { @@ -790,6 +830,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mUnfinishedBusiness = false; mCallbacks.releaseSuspendBlocker(); } + + // Record if dozing for future comparison. + mDozing = state != Display.STATE_ON; } @Override diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index 1b984a418257..dbccc0765b73 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -86,7 +86,6 @@ public final class DreamManagerService extends SystemService { private boolean mCurrentDreamCanDoze; private boolean mCurrentDreamIsDozing; private boolean mCurrentDreamIsWaking; - private Runnable mStopDreamRunnable; private int mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN; private int mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT; @@ -350,11 +349,6 @@ public final class DreamManagerService extends SystemService { private void startDreamLocked(final ComponentName name, final boolean isTest, final boolean canDoze, final int userId) { - if (mStopDreamRunnable != null) { - mHandler.removeCallbacks(mStopDreamRunnable); - mStopDreamRunnable = null; - } - if (Objects.equal(mCurrentDreamName, name) && mCurrentDreamIsTest == isTest && mCurrentDreamCanDoze == canDoze @@ -392,15 +386,13 @@ public final class DreamManagerService extends SystemService { mCurrentDreamIsWaking = true; } - mStopDreamRunnable = new Runnable() { + mHandler.post(new Runnable() { @Override public void run() { Slog.i(TAG, "Performing gentle wake from dream."); mController.stopDream(immediate); - mStopDreamRunnable = null; } - }; - mHandler.post(mStopDreamRunnable); + }); } } diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java index ac7b763600a7..d1aecb1b8d1d 100644 --- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java @@ -936,7 +936,7 @@ final class DefaultPermissionGrantPolicy { // permissions if the version on the system image does not declare them. if (!isDefaultPhoneOrSms && pkg.isUpdatedSystemApp()) { PackageSetting sysPs = mService.mSettings.getDisabledSystemPkgLPr(pkg.packageName); - if (sysPs != null) { + if (sysPs != null && sysPs.pkg != null) { if (sysPs.pkg.requestedPermissions.isEmpty()) { return; } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 8bd1f790e524..66c870c502c4 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -14952,7 +14952,7 @@ public class PackageManagerService extends IPackageManager.Stub * * @return true if verification should be performed */ - private boolean isVerificationEnabled(int userId, int installFlags) { + private boolean isVerificationEnabled(int userId, int installFlags, int installerUid) { if (!DEFAULT_VERIFY_ENABLE) { return false; } @@ -14973,6 +14973,23 @@ public class PackageManagerService extends IPackageManager.Stub android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) == 0) { return false; } + } else { + // only when not installed from ADB, skip verification for instant apps when + // the installer and verifier are the same. + if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) { + if (mInstantAppInstallerActivity != null + && mInstantAppInstallerActivity.packageName.equals( + mRequiredVerifierPackage)) { + try { + mContext.getSystemService(AppOpsManager.class) + .checkPackage(installerUid, mRequiredVerifierPackage); + if (DEBUG_VERIFY) { + Slog.i(TAG, "disable verification for instant app"); + } + return false; + } catch (SecurityException ignore) { } + } + } } if (ensureVerifyAppsEnabled) { @@ -15807,8 +15824,11 @@ public class PackageManagerService extends IPackageManager.Stub final int requiredUid = mRequiredVerifierPackage == null ? -1 : getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING, verifierUser.getIdentifier()); + final int installerUid = + verificationInfo == null ? -1 : verificationInfo.installerUid; if (!origin.existing && requiredUid != -1 - && isVerificationEnabled(verifierUser.getIdentifier(), installFlags)) { + && isVerificationEnabled( + verifierUser.getIdentifier(), installFlags, installerUid)) { final Intent verification = new Intent( Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index b1154227b204..a64ab438feed 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -223,6 +223,7 @@ public class UserManagerService extends IUserManager.Stub { // Tron counters private static final String TRON_GUEST_CREATED = "users_guest_created"; private static final String TRON_USER_CREATED = "users_user_created"; + private static final String TRON_DEMO_CREATED = "users_demo_created"; private final Context mContext; private final PackageManagerService mPm; @@ -2523,7 +2524,8 @@ public class UserManagerService extends IUserManager.Stub { addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL, android.Manifest.permission.MANAGE_USERS); - MetricsLogger.count(mContext, isGuest ? TRON_GUEST_CREATED : TRON_USER_CREATED, 1); + MetricsLogger.count(mContext, isGuest ? TRON_GUEST_CREATED + : (isDemo ? TRON_DEMO_CREATED : TRON_USER_CREATED), 1); } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 8eebb10d266d..f46aeac81073 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -7692,6 +7692,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { break; case HapticFeedbackConstants.CONTEXT_CLICK: return VibrationEffect.get(VibrationEffect.EFFECT_TICK); + case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE: + return VibrationEffect.get(VibrationEffect.EFFECT_TICK); default: return null; } diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 525e0ff1e8dd..71ecaf61da48 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -1107,7 +1107,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree mAppAnimator.lastFreezeDuration = 0; mService.mAppsFreezingScreen++; if (mService.mAppsFreezingScreen == 1) { - mService.startFreezingDisplayLocked(false, 0, 0); + mService.startFreezingDisplayLocked(false, 0, 0, getDisplayContent()); mService.mH.removeMessages(H.APP_FREEZE_TIMEOUT); mService.mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000); } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index b5476d7e056b..a8e0d761afc3 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -987,7 +987,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } if (!rotateSeamlessly) { - mService.startFreezingDisplayLocked(inTransaction, anim[0], anim[1]); + mService.startFreezingDisplayLocked(inTransaction, anim[0], anim[1], this); // startFreezingDisplayLocked can reset the ScreenRotationAnimation. screenRotationAnimation = mService.mAnimator.getScreenRotationAnimationLocked( mDisplayId); @@ -1162,6 +1162,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final int dh = displayInfo.logicalHeight; config.orientation = (dw <= dh) ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE; + config.setRotation(displayInfo.rotation); + config.screenWidthDp = (int)(mService.mPolicy.getConfigDisplayWidth(dw, dh, displayInfo.rotation, config.uiMode, mDisplayId) / mDisplayMetrics.density); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 1f7ef5014d36..bf8fabd326db 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -17,15 +17,13 @@ package com.android.server.wm; import static android.app.ActivityManager.ENABLE_TASK_SNAPSHOTS; -import static android.graphics.Bitmap.Config.ARGB_8888; -import static android.graphics.Bitmap.Config.HARDWARE; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManager.StackId; import android.app.ActivityManager.TaskSnapshot; +import android.content.pm.PackageManager; import android.graphics.Bitmap; -import android.graphics.Canvas; import android.graphics.GraphicBuffer; import android.graphics.Rect; import android.os.Environment; @@ -89,9 +87,16 @@ class TaskSnapshotController { private final ArraySet<Task> mTmpTasks = new ArraySet<>(); private final Handler mHandler = new Handler(); + /** + * Flag indicating whether we are running on an Android TV device. + */ + private final boolean mIsRunningOnTv; + TaskSnapshotController(WindowManagerService service) { mService = service; mCache = new TaskSnapshotCache(mService, mLoader); + mIsRunningOnTv = mService.mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_LEANBACK); } void systemReady() { @@ -112,7 +117,7 @@ class TaskSnapshotController { } private void handleClosingApps(ArraySet<AppWindowToken> closingApps) { - if (!ENABLE_TASK_SNAPSHOTS || ActivityManager.isLowRamDeviceStatic()) { + if (shouldDisableSnapshots()) { return; } @@ -188,6 +193,10 @@ class TaskSnapshotController { 1f /* scale */); } + private boolean shouldDisableSnapshots() { + return !ENABLE_TASK_SNAPSHOTS || ActivityManager.isLowRamDeviceStatic() || mIsRunningOnTv; + } + private Rect minRect(Rect rect1, Rect rect2) { return new Rect(Math.min(rect1.left, rect2.left), Math.min(rect1.top, rect2.top), @@ -301,7 +310,7 @@ class TaskSnapshotController { * Called when screen is being turned off. */ void screenTurningOff(ScreenOffListener listener) { - if (!ENABLE_TASK_SNAPSHOTS || ActivityManager.isLowRamDeviceStatic()) { + if (shouldDisableSnapshots()) { listener.onScreenOff(); return; } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 9d1b3d9ed81a..4b066c0cad6e 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -20,7 +20,6 @@ import static android.Manifest.permission.MANAGE_APP_TOKENS; import static android.Manifest.permission.READ_FRAME_BUFFER; import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS; import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; -import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; import static android.app.StatusBarManager.DISABLE_MASK; import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED; @@ -33,6 +32,7 @@ import static android.os.Process.THREAD_PRIORITY_DISPLAY; import static android.os.Process.myPid; import static android.os.UserHandle.USER_NULL; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.DOCKED_INVALID; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; @@ -601,7 +601,7 @@ public class WindowManagerService extends IWindowManager.Stub final UnknownAppVisibilityController mUnknownAppVisibilityController = new UnknownAppVisibilityController(this); - final TaskSnapshotController mTaskSnapshotController = new TaskSnapshotController(this); + final TaskSnapshotController mTaskSnapshotController; boolean mIsTouchDevice; @@ -724,6 +724,9 @@ public class WindowManagerService extends IWindowManager.Stub // For frozen screen animations. private int mExitAnimId, mEnterAnimId; + // The display that the rotation animation is applying to. + private int mFrozenDisplayId; + /** Skip repeated AppWindowTokens initialization. Note that AppWindowsToken's version of this * is a long initialized to Long.MIN_VALUE so that it doesn't match this value on startup. */ int mTransactionSequence; @@ -989,6 +992,7 @@ public class WindowManagerService extends IWindowManager.Stub mWindowPlacerLocked = new WindowSurfacePlacer(this); mPolicy = policy; + mTaskSnapshotController = new TaskSnapshotController(this); LocalServices.addService(WindowManagerPolicy.class, mPolicy); @@ -2449,7 +2453,7 @@ public class WindowManagerService extends IWindowManager.Stub } else { mPolicy.selectRotationAnimationLw(anim); } - startFreezingDisplayLocked(false, anim[0], anim[1]); + startFreezingDisplayLocked(false, anim[0], anim[1], displayContent); config = new Configuration(mTempConfiguration); } } @@ -5555,7 +5559,7 @@ public class WindowManagerService extends IWindowManager.Stub if (configChanged) { mWaitingForConfig = true; startFreezingDisplayLocked(false /* inTransaction */, 0 /* exitAnim */, - 0 /* enterAnim */); + 0 /* enterAnim */, displayContent); mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayId).sendToTarget(); } @@ -5862,6 +5866,12 @@ public class WindowManagerService extends IWindowManager.Stub } void startFreezingDisplayLocked(boolean inTransaction, int exitAnim, int enterAnim) { + startFreezingDisplayLocked(inTransaction, exitAnim, enterAnim, + getDefaultDisplayContentLocked()); + } + + void startFreezingDisplayLocked(boolean inTransaction, int exitAnim, int enterAnim, + DisplayContent displayContent) { if (mDisplayFrozen) { return; } @@ -5882,6 +5892,10 @@ public class WindowManagerService extends IWindowManager.Stub mDisplayFreezeTime = SystemClock.elapsedRealtime(); mLastFinishedFreezeSource = null; + // {@link mDisplayFrozen} prevents us from freezing on multiple displays at the same time. + // As a result, we only track the display that has initially froze the screen. + mFrozenDisplayId = displayContent.getDisplayId(); + mInputMonitor.freezeInputDispatchingLw(); // Clear the last input window -- that is just used for @@ -5901,10 +5915,8 @@ public class WindowManagerService extends IWindowManager.Stub if (CUSTOM_SCREEN_ROTATION) { mExitAnimId = exitAnim; mEnterAnimId = enterAnim; - final DisplayContent displayContent = getDefaultDisplayContentLocked(); - final int displayId = displayContent.getDisplayId(); ScreenRotationAnimation screenRotationAnimation = - mAnimator.getScreenRotationAnimationLocked(displayId); + mAnimator.getScreenRotationAnimationLocked(mFrozenDisplayId); if (screenRotationAnimation != null) { screenRotationAnimation.kill(); } @@ -5915,8 +5927,10 @@ public class WindowManagerService extends IWindowManager.Stub // TODO(multidisplay): rotation on main screen only. displayContent.updateDisplayInfo(); screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent, - mFxSession, inTransaction, mPolicy.isDefaultOrientationForced(), isSecure, this); - mAnimator.setScreenRotationAnimationLocked(displayId, screenRotationAnimation); + mFxSession, inTransaction, mPolicy.isDefaultOrientationForced(), isSecure, + this); + mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId, + screenRotationAnimation); } } @@ -5940,6 +5954,13 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_ORIENTATION) Slog.d(TAG_WM, "stopFreezingDisplayLocked: Unfreezing now"); + final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId); + + // We must make a local copy of the displayId as it can be potentially overwritten later on + // in this method. For example, {@link startFreezingDisplayLocked} may be called as a result + // of update rotation, but we reference the frozen display after that call in this method. + final int displayId = mFrozenDisplayId; + mFrozenDisplayId = INVALID_DISPLAY; mDisplayFrozen = false; mLastDisplayFreezeDuration = (int)(SystemClock.elapsedRealtime() - mDisplayFreezeTime); StringBuilder sb = new StringBuilder(128); @@ -5958,8 +5979,6 @@ public class WindowManagerService extends IWindowManager.Stub boolean updateRotation = false; - final DisplayContent displayContent = getDefaultDisplayContentLocked(); - final int displayId = displayContent.getDisplayId(); ScreenRotationAnimation screenRotationAnimation = mAnimator.getScreenRotationAnimationLocked(displayId); if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index f74948f8e7f0..1d08c2ec9151 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2231,15 +2231,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWinAnimator.applyEnterAnimationLocked(); } - if (isConfigChanged()) { - final Configuration globalConfig = mService.mRoot.getConfiguration(); - final Configuration overrideConfig = getMergedOverrideConfiguration(); - mergedConfiguration.setConfiguration(globalConfig, overrideConfig); - if (DEBUG_CONFIGURATION) Slog.i(TAG, "Window " + this - + " visible with new global config: " + globalConfig - + " merged override config: " + overrideConfig); - mLastReportedConfiguration.setTo(getConfiguration()); - } + // always report back the new configuration + final Configuration globalConfig = mService.mRoot.getConfiguration(); + final Configuration overrideConfig = getMergedOverrideConfiguration(); + mergedConfiguration.setConfiguration(globalConfig, overrideConfig); + if (DEBUG_CONFIGURATION) Slog.i(TAG, "Window " + this + + " reporting new global config: " + globalConfig + + " merged override config: " + overrideConfig); + mLastReportedConfiguration.setTo(getConfiguration()); } void adjustStartingWindowFlags() { diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk index 362dd9fbb17a..a3d28bbd3754 100644 --- a/services/core/jni/Android.mk +++ b/services/core/jni/Android.mk @@ -95,6 +95,7 @@ LOCAL_SHARED_LIBRARIES += \ android.hardware.ir@1.0 \ android.hardware.light@2.0 \ android.hardware.power@1.0 \ + android.hardware.power@1.1 \ android.hardware.tetheroffload.config@1.0 \ android.hardware.thermal@1.0 \ android.hardware.tv.cec@1.0 \ diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp index 57bb9fedc135..37ae78254ce2 100644 --- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp +++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp @@ -29,6 +29,7 @@ #include <unistd.h> #include <android/hardware/power/1.0/IPower.h> +#include <android/hardware/power/1.1/IPower.h> #include <android_runtime/AndroidRuntime.h> #include <jni.h> @@ -46,6 +47,8 @@ using android::hardware::power::V1_0::IPower; using android::hardware::power::V1_0::PowerStatePlatformSleepState; using android::hardware::power::V1_0::PowerStateVoter; using android::hardware::power::V1_0::Status; +using android::hardware::power::V1_1::PowerStateSubsystem; +using android::hardware::power::V1_1::PowerStateSubsystemSleepState; using android::hardware::hidl_vec; namespace android @@ -263,9 +266,105 @@ static jint getPlatformLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject o return total_added; } +static jint getSubsystemLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) { + char *output = (char*)env->GetDirectBufferAddress(outBuf); + char *offset = output; + int remaining = (int)env->GetDirectBufferCapacity(outBuf); + int total_added = -1; + + //This is a IPower 1.1 API + sp<android::hardware::power::V1_1::IPower> gPowerHal_1_1 = nullptr; + + if (outBuf == NULL) { + jniThrowException(env, "java/lang/NullPointerException", "null argument"); + return -1; + } + + { + std::lock_guard<std::mutex> lock(gPowerHalMutex); + if (!getPowerHal()) { + ALOGE("Power Hal not loaded"); + return -1; + } + + //Trying to cast to 1.1, this will succeed only for devices supporting 1.1 + gPowerHal_1_1 = android::hardware::power::V1_1::IPower::castFrom(gPowerHal); + if (gPowerHal_1_1 == nullptr) { + //This device does not support IPower@1.1, exiting gracefully + return 0; + } + + Return<void> ret = gPowerHal_1_1->getSubsystemLowPowerStats( + [&offset, &remaining, &total_added](hidl_vec<PowerStateSubsystem> subsystems, + Status status) { + + if (status != Status::SUCCESS) + return; + + for (size_t i = 0; i < subsystems.size(); i++) { + int added; + const PowerStateSubsystem &subsystem = subsystems[i]; + + added = snprintf(offset, remaining, + "subsystem_%zu name=%s ", i + 1, subsystem.name.c_str()); + if (added < 0) { + break; + } + + if (added > remaining) { + added = remaining; + } + + offset += added; + remaining -= added; + total_added += added; + + for (size_t j = 0; j < subsystem.states.size(); j++) { + const PowerStateSubsystemSleepState& state = subsystem.states[j]; + added = snprintf(offset, remaining, + "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " last entry TS(ms)=%" PRIu64 " ", + j + 1, state.name.c_str(), state.residencyInMsecSinceBoot, + state.totalTransitions, state.lastEntryTimestampMs); + if (added < 0) { + break; + } + + if (added > remaining) { + added = remaining; + } + + offset += added; + remaining -= added; + total_added += added; + } + + if (remaining <= 0) { + /* rewrite NULL character*/ + offset--; + total_added--; + ALOGE("PowerHal: buffer not enough"); + break; + } + } + } + ); + + if (!ret.isOk()) { + ALOGE("getSubsystemLowPowerStats() failed: power HAL service not available"); + gPowerHal = nullptr; + return -1; + } + } + + *offset = 0; + total_added += 1; + return total_added; +} + static const JNINativeMethod method_table[] = { { "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup }, { "getPlatformLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getPlatformLowPowerStats }, + { "getSubsystemLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getSubsystemLowPowerStats }, }; int register_android_server_BatteryStatsService(JNIEnv *env) diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index 686dad4a8340..e12032d8d4c0 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -52,6 +52,10 @@ <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" /> <uses-permission android:name="android.permission.DELETE_PACKAGES" /> + <!-- Uses API introduced in O (26) --> + <uses-sdk android:minSdkVersion="1" + android:targetSdkVersion="26"/> + <application> <uses-library android:name="android.test.runner" /> diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java index 36e9b3f8a9e4..791d3e997f3b 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java @@ -1484,6 +1484,31 @@ public class AccountManagerServiceTest extends AndroidTestCase { } @SmallTest + public void testGetAccountsByTypeForPackageWhenTypeIsNull() throws Exception { + unlockSystemUser(); + HashMap<String, Integer> visibility1 = new HashMap<>(); + visibility1.put(AccountManagerServiceTestFixtures.CALLER_PACKAGE, + AccountManager.VISIBILITY_USER_MANAGED_VISIBLE); + + HashMap<String, Integer> visibility2 = new HashMap<>(); + visibility2.put(AccountManagerServiceTestFixtures.CALLER_PACKAGE, + AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE); + + mAms.addAccountExplicitlyWithVisibility( + AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "P11", null, visibility1); + mAms.addAccountExplicitlyWithVisibility( + AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE, "P12", null, visibility2); + + Account[] accounts = mAms.getAccountsByTypeForPackage( + null, "otherPackageName", + AccountManagerServiceTestFixtures.CALLER_PACKAGE); + // Only get the USER_MANAGED_NOT_VISIBLE account. + assertEquals(1, accounts.length); + assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS, accounts[0].name); + assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, accounts[0].type); + } + + @SmallTest public void testGetAuthTokenLabelWithNullAccountType() throws Exception { unlockSystemUser(); try { @@ -2341,6 +2366,224 @@ public class AccountManagerServiceTest extends AndroidTestCase { } @SmallTest + public void testGetAccountByTypeAndFeaturesWithNullResponse() throws Exception { + unlockSystemUser(); + try { + mAms.getAccountByTypeAndFeatures( + null, // response + AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, + AccountManagerServiceTestFixtures.ACCOUNT_FEATURES, + "testpackage"); // opPackageName + fail("IllegalArgumentException expected. But no exception was thrown."); + } catch (IllegalArgumentException e) { + // IllegalArgumentException is expected. + } + } + + @SmallTest + public void testGetAccountByTypeAndFeaturesWithNullAccountType() throws Exception { + unlockSystemUser(); + try { + mAms.getAccountByTypeAndFeatures( + mMockAccountManagerResponse, // response + null, // accountType + AccountManagerServiceTestFixtures.ACCOUNT_FEATURES, + "testpackage"); // opPackageName + fail("IllegalArgumentException expected. But no exception was thrown."); + } catch (IllegalArgumentException e) { + // IllegalArgumentException is expected. + } + } + + @SmallTest + public void testGetAccountByTypeAndFeaturesWithNoFeaturesAndNoAccount() throws Exception { + unlockSystemUser(); + mAms.getAccountByTypeAndFeatures( + mMockAccountManagerResponse, + AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, + null, + "testpackage"); + verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture()); + Bundle result = mBundleCaptor.getValue(); + String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME); + String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE); + assertEquals(null, accountName); + assertEquals(null, accountType); + } + + @SmallTest + public void testGetAccountByTypeAndFeaturesWithNoFeaturesAndOneVisibleAccount() + throws Exception { + unlockSystemUser(); + mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "p11", null); + mAms.getAccountByTypeAndFeatures( + mMockAccountManagerResponse, + AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, + null, + "testpackage"); + verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture()); + Bundle result = mBundleCaptor.getValue(); + String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME); + String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE); + assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS, accountName); + assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, accountType); + } + + @SmallTest + public void testGetAccountByTypeAndFeaturesWithNoFeaturesAndOneNotVisibleAccount() + throws Exception { + unlockSystemUser(); + HashMap<String, Integer> visibility = new HashMap<>(); + visibility.put(AccountManagerServiceTestFixtures.CALLER_PACKAGE, + AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE); + mAms.addAccountExplicitlyWithVisibility( + AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "p11", null, visibility); + mAms.getAccountByTypeAndFeatures( + mMockAccountManagerResponse, + AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, + null, + AccountManagerServiceTestFixtures.CALLER_PACKAGE); + verify(mMockContext).startActivityAsUser(mIntentCaptor.capture(), eq(UserHandle.SYSTEM)); + Intent intent = mIntentCaptor.getValue(); + Account[] accounts = (Account[]) intent.getExtra(AccountManager.KEY_ACCOUNTS); + assertEquals(1, accounts.length); + assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, accounts[0]); + } + + @SmallTest + public void testGetAccountByTypeAndFeaturesWithNoFeaturesAndTwoAccounts() throws Exception { + unlockSystemUser(); + mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "p11", null); + mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE, "p12", null); + + mAms.getAccountByTypeAndFeatures( + mMockAccountManagerResponse, + AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, + null, + "testpackage"); + verify(mMockContext).startActivityAsUser(mIntentCaptor.capture(), eq(UserHandle.SYSTEM)); + Intent intent = mIntentCaptor.getValue(); + Account[] accounts = (Account[]) intent.getExtra(AccountManager.KEY_ACCOUNTS); + assertEquals(2, accounts.length); + if (accounts[0].equals(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS)) { + assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, accounts[0]); + assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE, accounts[1]); + } else { + assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE, accounts[0]); + assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, accounts[1]); + } + } + + @SmallTest + public void testGetAccountByTypeAndFeaturesWithFeaturesAndNoAccount() throws Exception { + unlockSystemUser(); + final CountDownLatch latch = new CountDownLatch(1); + mAms.getAccountByTypeAndFeatures( + mMockAccountManagerResponse, + AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, + AccountManagerServiceTestFixtures.ACCOUNT_FEATURES, + "testpackage"); + waitForLatch(latch); + verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture()); + Bundle result = mBundleCaptor.getValue(); + String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME); + String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE); + assertEquals(null, accountName); + assertEquals(null, accountType); + } + + @SmallTest + public void testGetAccountByTypeAndFeaturesWithFeaturesAndNoQualifiedAccount() + throws Exception { + unlockSystemUser(); + mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE, "p12", null); + final CountDownLatch latch = new CountDownLatch(1); + mAms.getAccountByTypeAndFeatures( + mMockAccountManagerResponse, + AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, + AccountManagerServiceTestFixtures.ACCOUNT_FEATURES, + "testpackage"); + waitForLatch(latch); + verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture()); + Bundle result = mBundleCaptor.getValue(); + String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME); + String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE); + assertEquals(null, accountName); + assertEquals(null, accountType); + } + + @SmallTest + public void testGetAccountByTypeAndFeaturesWithFeaturesAndOneQualifiedAccount() + throws Exception { + unlockSystemUser(); + mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "p11", null); + mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE, "p12", null); + final CountDownLatch latch = new CountDownLatch(1); + mAms.getAccountByTypeAndFeatures( + mMockAccountManagerResponse, + AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, + AccountManagerServiceTestFixtures.ACCOUNT_FEATURES, + "testpackage"); + waitForLatch(latch); + verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture()); + Bundle result = mBundleCaptor.getValue(); + String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME); + String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE); + assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS, accountName); + assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, accountType); + } + + @SmallTest + public void testGetAccountByTypeAndFeaturesWithFeaturesAndOneQualifiedNotVisibleAccount() + throws Exception { + unlockSystemUser(); + HashMap<String, Integer> visibility = new HashMap<>(); + visibility.put(AccountManagerServiceTestFixtures.CALLER_PACKAGE, + AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE); + mAms.addAccountExplicitlyWithVisibility( + AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "p11", null, visibility); + final CountDownLatch latch = new CountDownLatch(1); + mAms.getAccountByTypeAndFeatures( + mMockAccountManagerResponse, + AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, + AccountManagerServiceTestFixtures.ACCOUNT_FEATURES, + AccountManagerServiceTestFixtures.CALLER_PACKAGE); + waitForLatch(latch); + verify(mMockContext).startActivityAsUser(mIntentCaptor.capture(), eq(UserHandle.SYSTEM)); + Intent intent = mIntentCaptor.getValue(); + Account[] accounts = (Account[]) intent.getExtra(AccountManager.KEY_ACCOUNTS); + assertEquals(1, accounts.length); + assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, accounts[0]); + } + + @SmallTest + public void testGetAccountByTypeAndFeaturesWithFeaturesAndTwoQualifiedAccount() + throws Exception { + unlockSystemUser(); + mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "p11", null); + mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS_2, "p12", null); + mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE, "p13", null); + final CountDownLatch latch = new CountDownLatch(1); + mAms.getAccountByTypeAndFeatures( + mMockAccountManagerResponse, + AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, + AccountManagerServiceTestFixtures.ACCOUNT_FEATURES, + "testpackage"); + waitForLatch(latch); + verify(mMockContext).startActivityAsUser(mIntentCaptor.capture(), eq(UserHandle.SYSTEM)); + Intent intent = mIntentCaptor.getValue(); + Account[] accounts = (Account[]) intent.getExtra(AccountManager.KEY_ACCOUNTS); + assertEquals(2, accounts.length); + if (accounts[0].equals(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS)) { + assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, accounts[0]); + assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS_2, accounts[1]); + } else { + assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS_2, accounts[0]); + assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, accounts[1]); + } + } + + @SmallTest public void testGetAccountsByFeaturesWithNullResponse() throws Exception { unlockSystemUser(); try { diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java index d176a0d56fb1..73f30d9f9e79 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java @@ -34,6 +34,7 @@ public final class AccountManagerServiceTestFixtures { public static final String KEY_OPTIONS_BUNDLE = "account_manager_service_test:option_bundle_key"; public static final String ACCOUNT_NAME_SUCCESS = "success_on_return@fixture.com"; + public static final String ACCOUNT_NAME_SUCCESS_2 = "success_on_return_2@fixture.com"; public static final String ACCOUNT_NAME_INTERVENE = "intervene@fixture.com"; public static final String ACCOUNT_NAME_ERROR = "error@fixture.com"; @@ -69,6 +70,8 @@ public final class AccountManagerServiceTestFixtures { public static final Account ACCOUNT_SUCCESS = new Account(ACCOUNT_NAME_SUCCESS, ACCOUNT_TYPE_1); + public static final Account ACCOUNT_SUCCESS_2 = + new Account(ACCOUNT_NAME_SUCCESS_2, ACCOUNT_TYPE_1); public static final Account ACCOUNT_INTERVENE = new Account(ACCOUNT_NAME_INTERVENE, ACCOUNT_TYPE_1); public static final Account ACCOUNT_ERROR = diff --git a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java index eb839a2668f7..8106364477d9 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java +++ b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java @@ -242,6 +242,8 @@ public class TestAccountType1Authenticator extends AbstractAccountAuthenticator if (account.name.equals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS)) { // fill bundle with true. result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true); + } else if (account.name.equals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS_2)) { + result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true); } else if (account.name.equals(AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE)) { // fill bundle with false. result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false); diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 7a35cd3ac5c7..331328d63841 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -17,7 +17,9 @@ package android.telecom; import android.Manifest; import android.annotation.RequiresPermission; import android.annotation.SuppressAutoDoc; +import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -48,6 +50,7 @@ import java.util.List; * descriptions. */ @SuppressAutoDoc +@SystemService(Context.TELECOM_SERVICE) public class TelecomManager { /** @@ -763,6 +766,10 @@ public class TelecomManager { * @hide */ @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PHONE_STATE + }) public List<PhoneAccountHandle> getPhoneAccountsSupportingScheme(String uriScheme) { try { if (isServiceConnected()) { @@ -844,6 +851,7 @@ public class TelecomManager { * @hide */ @SystemApi + @SuppressLint("Doclava125") public List<PhoneAccountHandle> getPhoneAccountsForPackage() { try { if (isServiceConnected()) { @@ -971,6 +979,7 @@ public class TelecomManager { * @hide */ @SystemApi + @SuppressLint("Doclava125") public void clearPhoneAccounts() { clearAccounts(); } @@ -980,6 +989,7 @@ public class TelecomManager { * @hide */ @SystemApi + @SuppressLint("Doclava125") public void clearAccounts() { try { if (isServiceConnected()) { @@ -1011,6 +1021,7 @@ public class TelecomManager { * @hide */ @SystemApi + @SuppressLint("Doclava125") public ComponentName getDefaultPhoneApp() { try { if (isServiceConnected()) { @@ -1227,6 +1238,10 @@ public class TelecomManager { * @hide */ @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PHONE_STATE + }) public boolean isRinging() { try { if (isServiceConnected()) { @@ -1245,6 +1260,7 @@ public class TelecomManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean endCall() { try { if (isServiceConnected()) { @@ -1324,6 +1340,10 @@ public class TelecomManager { * @hide */ @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PHONE_STATE + }) public boolean isTtySupported() { try { if (isServiceConnected()) { @@ -1602,6 +1622,7 @@ public class TelecomManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void enablePhoneAccount(PhoneAccountHandle handle, boolean isEnabled) { ITelecomService service = getTelecomService(); if (service != null) { diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 9781fb16c2bb..237ed47cace1 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -18,7 +18,10 @@ package android.telephony; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.content.Context; import android.os.PersistableBundle; import android.os.RemoteException; @@ -29,14 +32,8 @@ import com.android.internal.telephony.ICarrierConfigLoader; /** * Provides access to telephony configuration values that are carrier-specific. - * <p> - * Users should obtain an instance of this class by calling - * {@code mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);} - * </p> - * - * @see Context#getSystemService - * @see Context#CARRIER_CONFIG_SERVICE */ +@SystemService(Context.CARRIER_CONFIG_SERVICE) public class CarrierConfigManager { private final static String TAG = "CarrierConfigManager"; @@ -1797,6 +1794,7 @@ public class CarrierConfigManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void updateConfigForPhoneId(int phoneId, String simState) { try { ICarrierConfigLoader loader = getICarrierConfigLoader(); @@ -1818,6 +1816,7 @@ public class CarrierConfigManager { */ @NonNull @SystemApi + @SuppressLint("Doclava125") public static PersistableBundle getDefaultConfig() { return new PersistableBundle(sDefaults); } diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index 7d4d90bb75ab..1eac263133e5 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -16,6 +16,7 @@ package android.telephony; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.app.ActivityThread; import android.app.PendingIntent; @@ -346,6 +347,7 @@ public final class SmsManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void sendTextMessageWithoutPersisting( String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) { @@ -530,6 +532,7 @@ public final class SmsManager { * @hide **/ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void sendMultipartTextMessageWithoutPersisting( String destinationAddress, String scAddress, List<String> parts, List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) { diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 77525ff50040..0d1764b7a80a 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -18,6 +18,7 @@ package android.telephony; import android.annotation.NonNull; import android.annotation.SdkConstant; +import android.annotation.SystemService; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.content.Intent; @@ -41,13 +42,11 @@ import java.util.List; /** * SubscriptionManager is the application interface to SubscriptionController * and provides information about the current Telephony Subscriptions. - * * <p> - * You do not instantiate this class directly; instead, you retrieve - * a reference to an instance through {@link #from}. * <p> * All SDK public methods require android.Manifest.permission.READ_PHONE_STATE unless otherwise * specified. */ +@SystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) public class SubscriptionManager { private static final String LOG_TAG = "SubscriptionManager"; private static final boolean DBG = false; diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 37c86a768ae7..74327cef6bd3 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -22,8 +22,10 @@ import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; +import android.annotation.SuppressLint; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.annotation.WorkerThread; import android.app.ActivityThread; import android.app.PendingIntent; @@ -80,11 +82,6 @@ import java.util.regex.Pattern; * types of subscriber information. Applications can also register * a listener to receive notification of telephony state changes. * <p> - * You do not instantiate this class directly; instead, you retrieve - * a reference to an instance through - * {@link android.content.Context#getSystemService - * Context.getSystemService(Context.TELEPHONY_SERVICE)}. - * * The returned TelephonyManager will use the default subscription for all calls. * To call an API for a specific subscription, use {@link #createForSubscriptionId(int)}. e.g. * <code> @@ -97,6 +94,7 @@ import java.util.regex.Pattern; * its manifest file. Where permissions apply, they are noted in the * the methods through which you access the protected information. */ +@SystemService(Context.TELEPHONY_SERVICE) public class TelephonyManager { private static final String TAG = "TelephonyManager"; @@ -2746,8 +2744,8 @@ public class TelephonyManager { * be implemented instead. */ @SystemApi + @SuppressLint("Doclava125") public void setVisualVoicemailEnabled(PhoneAccountHandle phoneAccountHandle, boolean enabled){ - } /** @@ -2761,6 +2759,7 @@ public class TelephonyManager { */ @SystemApi @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @SuppressLint("Doclava125") public boolean isVisualVoicemailEnabled(PhoneAccountHandle phoneAccountHandle){ return false; } @@ -2779,6 +2778,7 @@ public class TelephonyManager { * @hide */ @SystemApi + @SuppressLint("Doclava125") @Nullable public Bundle getVisualVoicemailSettings(){ try { @@ -4956,12 +4956,14 @@ public class TelephonyManager { /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn() { return getCdmaMdn(getSubId()); } /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(int subId) { try { ITelephony telephony = getITelephony(); @@ -4977,12 +4979,14 @@ public class TelephonyManager { /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin() { return getCdmaMin(getSubId()); } /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin(int subId) { try { ITelephony telephony = getITelephony(); @@ -4998,6 +5002,7 @@ public class TelephonyManager { /** @hide */ @SystemApi + @SuppressLint("Doclava125") public int checkCarrierPrivilegesForPackage(String pkgName) { try { ITelephony telephony = getITelephony(); @@ -5013,6 +5018,7 @@ public class TelephonyManager { /** @hide */ @SystemApi + @SuppressLint("Doclava125") public int checkCarrierPrivilegesForPackageAnyPhone(String pkgName) { try { ITelephony telephony = getITelephony(); @@ -5064,6 +5070,7 @@ public class TelephonyManager { /** @hide */ @SystemApi + @SuppressLint("Doclava125") public void dial(String number) { try { ITelephony telephony = getITelephony(); @@ -5076,6 +5083,7 @@ public class TelephonyManager { /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void call(String callingPackage, String number) { try { ITelephony telephony = getITelephony(); @@ -5088,6 +5096,7 @@ public class TelephonyManager { /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.CALL_PHONE) public boolean endCall() { try { ITelephony telephony = getITelephony(); @@ -5101,6 +5110,7 @@ public class TelephonyManager { /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void answerRingingCall() { try { ITelephony telephony = getITelephony(); @@ -5113,6 +5123,7 @@ public class TelephonyManager { /** @hide */ @SystemApi + @SuppressLint("Doclava125") public void silenceRinger() { try { getTelecomService().silenceRinger(getOpPackageName()); @@ -5123,6 +5134,10 @@ public class TelephonyManager { /** @hide */ @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PHONE_STATE + }) public boolean isOffhook() { try { ITelephony telephony = getITelephony(); @@ -5136,6 +5151,10 @@ public class TelephonyManager { /** @hide */ @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PHONE_STATE + }) public boolean isRinging() { try { ITelephony telephony = getITelephony(); @@ -5149,6 +5168,10 @@ public class TelephonyManager { /** @hide */ @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PHONE_STATE + }) public boolean isIdle() { try { ITelephony telephony = getITelephony(); @@ -5162,6 +5185,10 @@ public class TelephonyManager { /** @hide */ @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PHONE_STATE + }) public boolean isRadioOn() { try { ITelephony telephony = getITelephony(); @@ -5175,6 +5202,7 @@ public class TelephonyManager { /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean supplyPin(String pin) { try { ITelephony telephony = getITelephony(); @@ -5188,6 +5216,7 @@ public class TelephonyManager { /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean supplyPuk(String puk, String pin) { try { ITelephony telephony = getITelephony(); @@ -5201,6 +5230,7 @@ public class TelephonyManager { /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int[] supplyPinReportResult(String pin) { try { ITelephony telephony = getITelephony(); @@ -5214,6 +5244,7 @@ public class TelephonyManager { /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int[] supplyPukReportResult(String puk, String pin) { try { ITelephony telephony = getITelephony(); @@ -5333,6 +5364,7 @@ public class TelephonyManager { /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handlePinMmi(String dialString) { try { ITelephony telephony = getITelephony(); @@ -5346,6 +5378,7 @@ public class TelephonyManager { /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handlePinMmiForSubscriber(int subId, String dialString) { try { ITelephony telephony = getITelephony(); @@ -5359,6 +5392,7 @@ public class TelephonyManager { /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void toggleRadioOnOff() { try { ITelephony telephony = getITelephony(); @@ -5371,6 +5405,7 @@ public class TelephonyManager { /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadio(boolean turnOn) { try { ITelephony telephony = getITelephony(); @@ -5384,6 +5419,7 @@ public class TelephonyManager { /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadioPower(boolean turnOn) { try { ITelephony telephony = getITelephony(); @@ -5397,6 +5433,7 @@ public class TelephonyManager { /** @hide */ @SystemApi + @SuppressLint("Doclava125") public void updateServiceLocation() { try { ITelephony telephony = getITelephony(); @@ -5409,6 +5446,7 @@ public class TelephonyManager { /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean enableDataConnectivity() { try { ITelephony telephony = getITelephony(); @@ -5422,6 +5460,7 @@ public class TelephonyManager { /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean disableDataConnectivity() { try { ITelephony telephony = getITelephony(); @@ -5473,12 +5512,14 @@ public class TelephonyManager { * * @see #hasCarrierPrivileges */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(boolean enable) { setDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable); } /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int subId, boolean enable) { try { Log.d(TAG, "setDataEnabled: enabled=" + enable); @@ -5568,6 +5609,7 @@ public class TelephonyManager { /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void enableVideoCalling(boolean enable) { try { ITelephony telephony = getITelephony(); @@ -5580,6 +5622,10 @@ public class TelephonyManager { /** @hide */ @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PHONE_STATE + }) public boolean isVideoCallingEnabled() { try { ITelephony telephony = getITelephony(); @@ -6488,6 +6534,7 @@ public class TelephonyManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public List<TelephonyHistogram> getTelephonyHistograms() { try { ITelephony service = getITelephony(); @@ -6515,6 +6562,7 @@ public class TelephonyManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int slotIndex, List<CarrierIdentifier> carriers) { try { ITelephony service = getITelephony(); @@ -6533,9 +6581,6 @@ public class TelephonyManager { * Get the allowed carrier list for slotIndex. * Require system privileges. In the future we may add this to carrier APIs. * - * <p>Requires Permission: - * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} - * * <p>This method returns valid data on devices with {@link * android.content.pm.PackageManager#FEATURE_TELEPHONY_CARRIERLOCK} enabled. * @@ -6544,6 +6589,7 @@ public class TelephonyManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public List<CarrierIdentifier> getAllowedCarriers(int slotIndex) { try { ITelephony service = getITelephony(); diff --git a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java index fb7971e1e104..c535c455e7a8 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java @@ -17,15 +17,22 @@ package com.android.server.connectivity.tethering; import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; import android.content.Context; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.RouteInfo; import android.net.util.SharedLog; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; @@ -35,9 +42,13 @@ import android.support.test.runner.AndroidJUnit4; import android.test.mock.MockContentResolver; import com.android.internal.util.test.FakeSettingsProvider; +import java.net.InetAddress; +import java.util.ArrayList; + import org.junit.Before; import org.junit.runner.RunWith; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -49,6 +60,7 @@ public class OffloadControllerTest { @Mock private OffloadHardwareInterface mHardware; @Mock private Context mContext; + final ArgumentCaptor<ArrayList> mStringArrayCaptor = ArgumentCaptor.forClass(ArrayList.class); private MockContentResolver mContentResolver; @Before public void setUp() throws Exception { @@ -114,4 +126,87 @@ public class OffloadControllerTest { inOrder.verify(mHardware, never()).initOffloadControl(anyObject()); inOrder.verifyNoMoreInteractions(); } + + @Test + public void testSetUpstreamLinkPropertiesWorking() throws Exception { + setupFunctioningHardwareInterface(); + final OffloadController offload = + new OffloadController(null, mHardware, mContentResolver, new SharedLog("test")); + offload.start(); + + final InOrder inOrder = inOrder(mHardware); + inOrder.verify(mHardware, times(1)).initOffloadConfig(); + inOrder.verify(mHardware, times(1)).initOffloadControl( + any(OffloadHardwareInterface.ControlCallback.class)); + inOrder.verifyNoMoreInteractions(); + + offload.setUpstreamLinkProperties(null); + inOrder.verify(mHardware, times(1)).setUpstreamParameters( + eq(null), eq(null), eq(null), eq(null)); + inOrder.verifyNoMoreInteractions(); + reset(mHardware); + + final LinkProperties lp = new LinkProperties(); + + final String testIfName = "rmnet_data17"; + lp.setInterfaceName(testIfName); + offload.setUpstreamLinkProperties(lp); + inOrder.verify(mHardware, times(1)).setUpstreamParameters( + eq(testIfName), eq(null), eq(null), mStringArrayCaptor.capture()); + assertTrue(mStringArrayCaptor.getValue().isEmpty()); + inOrder.verifyNoMoreInteractions(); + + final String ipv4Addr = "192.0.2.5"; + final String linkAddr = ipv4Addr + "/24"; + lp.addLinkAddress(new LinkAddress(linkAddr)); + offload.setUpstreamLinkProperties(lp); + inOrder.verify(mHardware, times(1)).setUpstreamParameters( + eq(testIfName), eq(ipv4Addr), eq(null), mStringArrayCaptor.capture()); + assertTrue(mStringArrayCaptor.getValue().isEmpty()); + inOrder.verifyNoMoreInteractions(); + + final String ipv4Gateway = "192.0.2.1"; + lp.addRoute(new RouteInfo(InetAddress.getByName(ipv4Gateway))); + offload.setUpstreamLinkProperties(lp); + inOrder.verify(mHardware, times(1)).setUpstreamParameters( + eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); + assertTrue(mStringArrayCaptor.getValue().isEmpty()); + inOrder.verifyNoMoreInteractions(); + + final String ipv6Gw1 = "fe80::cafe"; + lp.addRoute(new RouteInfo(InetAddress.getByName(ipv6Gw1))); + offload.setUpstreamLinkProperties(lp); + inOrder.verify(mHardware, times(1)).setUpstreamParameters( + eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); + ArrayList<String> v6gws = mStringArrayCaptor.getValue(); + assertEquals(1, v6gws.size()); + assertTrue(v6gws.contains(ipv6Gw1)); + inOrder.verifyNoMoreInteractions(); + + final String ipv6Gw2 = "fe80::d00d"; + lp.addRoute(new RouteInfo(InetAddress.getByName(ipv6Gw2))); + offload.setUpstreamLinkProperties(lp); + inOrder.verify(mHardware, times(1)).setUpstreamParameters( + eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); + v6gws = mStringArrayCaptor.getValue(); + assertEquals(2, v6gws.size()); + assertTrue(v6gws.contains(ipv6Gw1)); + assertTrue(v6gws.contains(ipv6Gw2)); + inOrder.verifyNoMoreInteractions(); + + final LinkProperties stacked = new LinkProperties(); + stacked.setInterfaceName("stacked"); + stacked.addLinkAddress(new LinkAddress("192.0.2.129/25")); + stacked.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254"))); + stacked.addRoute(new RouteInfo(InetAddress.getByName("fe80::bad:f00"))); + assertTrue(lp.addStackedLink(stacked)); + offload.setUpstreamLinkProperties(lp); + inOrder.verify(mHardware, times(1)).setUpstreamParameters( + eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); + v6gws = mStringArrayCaptor.getValue(); + assertEquals(2, v6gws.size()); + assertTrue(v6gws.contains(ipv6Gw1)); + assertTrue(v6gws.contains(ipv6Gw2)); + inOrder.verifyNoMoreInteractions(); + } } diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp index 5352ca8679b3..c6382b177f42 100644 --- a/tools/aapt2/ResourceParser_test.cpp +++ b/tools/aapt2/ResourceParser_test.cpp @@ -843,4 +843,9 @@ TEST_F(ResourceParserTest, ParseElementWithNoValue) { EXPECT_THAT(*str->value, Eq("")); } +TEST_F(ResourceParserTest, ParsePlatformIndependentNewline) { + std::string input = R"(<string name="foo">%1$s %n %2$s</string>)"; + ASSERT_TRUE(TestParse(input)); +} + } // namespace aapt diff --git a/tools/aapt2/integration-tests/AppOne/AndroidManifest.xml b/tools/aapt2/integration-tests/AppOne/AndroidManifest.xml index 1a4067fb0b22..a5f202dd22fc 100644 --- a/tools/aapt2/integration-tests/AppOne/AndroidManifest.xml +++ b/tools/aapt2/integration-tests/AppOne/AndroidManifest.xml @@ -19,4 +19,11 @@ <uses-sdk android:minSdkVersion="21" /> <uses-permission-sdk-23 android:name="android.permission.TEST" android:maxSdkVersion="22" /> + + <application> + <activity android:name=".MyActivity"> + <layout android:defaultHeight="500dp" + android:defaultWidth="600dp" /> + </activity> + </application> </manifest> diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index 53c66a628568..99fd95be2571 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -326,7 +326,10 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, uses_static_library_action.Action(RequiredAndroidAttribute("certDigest")); application_action["meta-data"] = meta_data_action; + application_action["activity"] = component_action; + application_action["activity"]["layout"]; + application_action["activity-alias"] = component_action; application_action["service"] = component_action; application_action["receiver"] = component_action; diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp index cf2232254ec4..28e952e25a67 100644 --- a/tools/aapt2/util/Util.cpp +++ b/tools/aapt2/util/Util.cpp @@ -203,7 +203,7 @@ bool VerifyJavaStringFormat(const StringPiece& str) { if (*c == '%' && c + 1 < end) { c++; - if (*c == '%') { + if (*c == '%' || *c == 'n') { c++; continue; } diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java index b133a44d0117..a4b3bf2a3019 100644 --- a/wifi/java/android/net/wifi/RttManager.java +++ b/wifi/java/android/net/wifi/RttManager.java @@ -1,7 +1,10 @@ package android.net.wifi; import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.content.Context; import android.os.Bundle; import android.os.Handler; @@ -20,6 +23,7 @@ import com.android.internal.util.Protocol; /** @hide */ @SystemApi +@SystemService(Context.WIFI_RTT_SERVICE) public class RttManager { private static final boolean DBG = false; @@ -167,6 +171,7 @@ public class RttManager { /** @deprecated Use the new {@link android.net.wifi.RttManager#getRttCapabilities()} API.*/ @Deprecated + @SuppressLint("Doclava125") public Capabilities getCapabilities() { return new Capabilities(); } @@ -990,7 +995,7 @@ public class RttManager { * @exception throw IllegalArgumentException when params are illegal * throw IllegalStateException when RttCapabilities do not exist */ - + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startRanging(RttParams[] params, RttListener listener) { int index = 0; for(RttParams rttParam : params) { @@ -1006,6 +1011,7 @@ public class RttManager { 0, putListener(listener), parcelableParams); } + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void stopRanging(RttListener listener) { validateChannel(); mAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener)); @@ -1039,6 +1045,7 @@ public class RttManager { * @param callback Callback for responder enabling/disabling result. * @throws IllegalArgumentException If {@code callback} is null. */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void enableResponder(ResponderCallback callback) { if (callback == null) { throw new IllegalArgumentException("callback cannot be null"); @@ -1058,6 +1065,7 @@ public class RttManager { * @param callback The same callback used for enabling responder. * @throws IllegalArgumentException If {@code callback} is null. */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void disableResponder(ResponderCallback callback) { if (callback == null) { throw new IllegalArgumentException("callback cannot be null"); diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index f7333e281982..91fc2f7ce773 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -67,6 +67,8 @@ public class WifiConfiguration implements Parcelable { public static final String updateIdentiferVarName = "update_identifier"; /** {@hide} */ public static final int INVALID_NETWORK_ID = -1; + /** {@hide} */ + public static final int LOCAL_ONLY_NETWORK_ID = -2; /** {@hide} */ private String mPasspointManagementObjectTree; diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 58df4ee227f2..c89a9a458393 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -17,9 +17,12 @@ package android.net.wifi; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; +import android.annotation.SuppressLint; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.content.Context; import android.content.pm.ParceledListSlice; import android.net.ConnectivityManager; @@ -56,27 +59,30 @@ import java.util.concurrent.CountDownLatch; /** * This class provides the primary API for managing all aspects of Wi-Fi - * connectivity. Get an instance of this class by calling - * {@link android.content.Context#getSystemService(String) Context.getSystemService(Context.WIFI_SERVICE)}. - * On releases before NYC, it should only be obtained from an application context, and not from - * any other derived context to avoid memory leaks within the calling process. - + * connectivity. + * <p> + * On releases before {@link android.os.Build.VERSION_CODES#N}, this object + * should only be obtained from an {@linkplain Context#getApplicationContext() + * application context}, and not from any other derived context to avoid memory + * leaks within the calling process. + * <p> * It deals with several categories of items: * <ul> - * <li>The list of configured networks. The list can be viewed and updated, - * and attributes of individual entries can be modified.</li> + * <li>The list of configured networks. The list can be viewed and updated, and + * attributes of individual entries can be modified.</li> * <li>The currently active Wi-Fi network, if any. Connectivity can be - * established or torn down, and dynamic information about the state of - * the network can be queried.</li> - * <li>Results of access point scans, containing enough information to - * make decisions about what access point to connect to.</li> - * <li>It defines the names of various Intent actions that are broadcast - * upon any sort of change in Wi-Fi state. + * established or torn down, and dynamic information about the state of the + * network can be queried.</li> + * <li>Results of access point scans, containing enough information to make + * decisions about what access point to connect to.</li> + * <li>It defines the names of various Intent actions that are broadcast upon + * any sort of change in Wi-Fi state. * </ul> - * This is the API to use when performing Wi-Fi specific operations. To - * perform operations that pertain to network connectivity at an abstract - * level, use {@link android.net.ConnectivityManager}. + * This is the API to use when performing Wi-Fi specific operations. To perform + * operations that pertain to network connectivity at an abstract level, use + * {@link android.net.ConnectivityManager}. */ +@SystemService(Context.WIFI_SERVICE) public class WifiManager { private static final String TAG = "WifiManager"; @@ -966,6 +972,7 @@ public class WifiManager { /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.READ_WIFI_CREDENTIAL) public List<WifiConfiguration> getPrivilegedConfiguredNetworks() { try { ParceledListSlice<WifiConfiguration> parceledList = @@ -981,6 +988,7 @@ public class WifiManager { /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.READ_WIFI_CREDENTIAL) public WifiConnectionStatistics getConnectionStatistics() { try { return mService.getConnectionStatistics(); @@ -1522,6 +1530,7 @@ public class WifiManager { /** @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public boolean startScan(WorkSource workSource) { try { String packageName = mContext.getOpPackageName(); @@ -1542,6 +1551,7 @@ public class WifiManager { */ @Deprecated @SystemApi + @SuppressLint("Doclava125") public boolean startLocationRestrictedScan(WorkSource workSource) { return false; } @@ -1556,6 +1566,7 @@ public class WifiManager { */ @Deprecated @SystemApi + @SuppressLint("Doclava125") public boolean isBatchedScanSupported() { return false; } @@ -1569,6 +1580,7 @@ public class WifiManager { */ @Deprecated @SystemApi + @SuppressLint("Doclava125") public List<BatchedScanResult> getBatchedScanResults() { return null; } @@ -1808,6 +1820,7 @@ public class WifiManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) { String packageName = mContext.getOpPackageName(); @@ -2053,6 +2066,7 @@ public class WifiManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getWifiApState() { try { return mService.getWifiApEnabledState(); @@ -2069,6 +2083,7 @@ public class WifiManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiApEnabled() { return getWifiApState() == WIFI_AP_STATE_ENABLED; } @@ -2080,6 +2095,7 @@ public class WifiManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public WifiConfiguration getWifiApConfiguration() { try { return mService.getWifiApConfiguration(); @@ -2095,6 +2111,7 @@ public class WifiManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) { try { mService.setWifiApConfiguration(wifiConfig); diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java index c02ceef95b73..f47d5caf182b 100644 --- a/wifi/java/android/net/wifi/WifiScanner.java +++ b/wifi/java/android/net/wifi/WifiScanner.java @@ -16,7 +16,10 @@ package android.net.wifi; +import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.annotation.SystemService; import android.content.Context; import android.os.Bundle; import android.os.Handler; @@ -40,12 +43,10 @@ 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 - * {@link android.content.Context#getSystemService(String) Context.getSystemService(Context - * .WIFI_SCANNING_SERVICE)}. * @hide */ @SystemApi +@SystemService(Context.WIFI_SCANNING_SERVICE) public class WifiScanner { /** no band specified; use channel list instead */ @@ -732,6 +733,7 @@ public class WifiScanner { * key for this scan, and must also be specified to cancel the scan. Multiple * scans should also not share this object. */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startBackgroundScan(ScanSettings settings, ScanListener listener) { startBackgroundScan(settings, listener, null); } @@ -744,6 +746,7 @@ public class WifiScanner { * key for this scan, and must also be specified to cancel the scan. Multiple * scans should also not share this object. */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startBackgroundScan(ScanSettings settings, ScanListener listener, WorkSource workSource) { Preconditions.checkNotNull(listener, "listener cannot be null"); @@ -761,6 +764,7 @@ public class WifiScanner { * @param listener specifies which scan to cancel; must be same object as passed in {@link * #startBackgroundScan} */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void stopBackgroundScan(ScanListener listener) { Preconditions.checkNotNull(listener, "listener cannot be null"); int key = removeListener(listener); @@ -772,6 +776,7 @@ public class WifiScanner { * reports currently available scan results on appropriate listeners * @return true if all scan results were reported correctly */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean getScanResults() { validateChannel(); Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0); @@ -786,6 +791,7 @@ public class WifiScanner { * key for this scan, and must also be specified to cancel the scan. Multiple * scans should also not share this object. */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startScan(ScanSettings settings, ScanListener listener) { startScan(settings, listener, null); } @@ -799,6 +805,7 @@ public class WifiScanner { * key for this scan, and must also be specified to cancel the scan. Multiple * scans should also not share this object. */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startScan(ScanSettings settings, ScanListener listener, WorkSource workSource) { Preconditions.checkNotNull(listener, "listener cannot be null"); int key = addListener(listener); @@ -815,6 +822,7 @@ public class WifiScanner { * hasn't been called on the listener, ignored otherwise * @param listener */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void stopScan(ScanListener listener) { Preconditions.checkNotNull(listener, "listener cannot be null"); int key = removeListener(listener); @@ -962,6 +970,7 @@ public class WifiScanner { * @param bssidInfos access points to watch */ @Deprecated + @SuppressLint("Doclava125") public void configureWifiChange( int rssiSampleSize, /* sample size for RSSI averaging */ int lostApSampleSize, /* samples to confirm AP's loss */ @@ -995,6 +1004,7 @@ public class WifiScanner { * provided on {@link #stopTrackingWifiChange} */ @Deprecated + @SuppressLint("Doclava125") public void startTrackingWifiChange(WifiChangeListener listener) { throw new UnsupportedOperationException(); } @@ -1005,6 +1015,7 @@ public class WifiScanner { * #stopTrackingWifiChange} */ @Deprecated + @SuppressLint("Doclava125") public void stopTrackingWifiChange(WifiChangeListener listener) { throw new UnsupportedOperationException(); } @@ -1012,6 +1023,7 @@ public class WifiScanner { /** @hide */ @SystemApi @Deprecated + @SuppressLint("Doclava125") public void configureWifiChange(WifiChangeSettings settings) { throw new UnsupportedOperationException(); } @@ -1067,6 +1079,7 @@ public class WifiScanner { * also be provided on {@link #stopTrackingBssids} */ @Deprecated + @SuppressLint("Doclava125") public void startTrackingBssids(BssidInfo[] bssidInfos, int apLostThreshold, BssidListener listener) { throw new UnsupportedOperationException(); @@ -1077,6 +1090,7 @@ public class WifiScanner { * @param listener same object provided in {@link #startTrackingBssids} */ @Deprecated + @SuppressLint("Doclava125") public void stopTrackingBssids(BssidListener listener) { throw new UnsupportedOperationException(); } diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java index 87f199292dd8..df0d9d23fe14 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; +import android.annotation.SystemService; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.net.ConnectivityManager; @@ -49,9 +50,7 @@ import java.util.List; /** * This class provides the primary API for managing Wi-Fi Aware operations: - * discovery and peer-to-peer data connections. Get an instance of this class by calling - * {@link android.content.Context#getSystemService(String) - * Context.getSystemService(Context.WIFI_AWARE_SERVICE)}. + * discovery and peer-to-peer data connections. * <p> * The class provides access to: * <ul> @@ -121,6 +120,7 @@ import java.util.List; * {@link DiscoverySession#createNetworkSpecifierPassphrase(PeerHandle, String)}. * </ul> */ +@SystemService(Context.WIFI_AWARE_SERVICE) public class WifiAwareManager { private static final String TAG = "WifiAwareManager"; private static final boolean DBG = false; diff --git a/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl b/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl index 8b1cfaee8119..bfdd45d9f9b0 100644 --- a/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl +++ b/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl @@ -25,8 +25,9 @@ import android.os.Messenger; */ interface IWifiP2pManager { - Messenger getMessenger(); + Messenger getMessenger(in IBinder binder); Messenger getP2pStateMachineMessenger(); + oneway void close(in IBinder binder); void setMiracastMode(int mode); void checkConfigureWifiDisplayPermission(); } diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java index c93ac7b5f8f1..7f085f71e99c 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java @@ -17,6 +17,7 @@ package android.net.wifi.p2p; import android.annotation.SdkConstant; +import android.annotation.SystemService; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.net.wifi.WpsInfo; @@ -27,6 +28,7 @@ import android.net.wifi.p2p.nsd.WifiP2pServiceRequest; import android.net.wifi.p2p.nsd.WifiP2pServiceResponse; import android.net.wifi.p2p.nsd.WifiP2pUpnpServiceInfo; import android.net.wifi.p2p.nsd.WifiP2pUpnpServiceResponse; +import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -119,9 +121,6 @@ import java.util.Map; * {@link android.Manifest.permission#CHANGE_WIFI_STATE} to perform any further peer-to-peer * operations. * - * Get an instance of this class by calling {@link android.content.Context#getSystemService(String) - * Context.getSystemService(Context.WIFI_P2P_SERVICE)}. - * * {@see WifiP2pConfig} * {@see WifiP2pInfo} * {@see WifiP2pGroup} @@ -129,6 +128,7 @@ import java.util.Map; * {@see WifiP2pDeviceList} * {@see android.net.wifi.WpsInfo} */ +@SystemService(Context.WIFI_P2P_SERVICE) public class WifiP2pManager { private static final String TAG = "WifiP2pManager"; /** @@ -291,6 +291,7 @@ public class WifiP2pManager { "android.net.wifi.p2p.CALLING_PACKAGE"; IWifiP2pManager mService; + private final Map<Channel, Binder> mBinders = new HashMap<>(); private static final int BASE = Protocol.BASE_WIFI_P2P_MANAGER; @@ -890,7 +891,10 @@ public class WifiP2pManager { * @return Channel instance that is necessary for performing any further p2p operations */ public Channel initialize(Context srcContext, Looper srcLooper, ChannelListener listener) { - return initalizeChannel(srcContext, srcLooper, listener, getMessenger()); + Binder binder = new Binder(); + Channel channel = initalizeChannel(srcContext, srcLooper, listener, getMessenger(binder)); + mBinders.put(channel, binder); + return channel; } /** @@ -1386,12 +1390,14 @@ public class WifiP2pManager { * Get a reference to WifiP2pService handler. This is used to establish * an AsyncChannel communication with WifiService * + * @param binder A binder for the service to associate with this client. + * * @return Messenger pointing to the WifiP2pService handler * @hide */ - public Messenger getMessenger() { + public Messenger getMessenger(Binder binder) { try { - return mService.getMessenger(); + return mService.getMessenger(binder); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1413,6 +1419,23 @@ public class WifiP2pManager { } /** + * Close the current P2P connection and clean-up any configuration requested by the + * current app. Takes same action as taken when the app dies. + * + * @param c is the channel created at {@link #initialize} + * + * @hide + */ + public void close(Channel c) { + try { + mService.close(mBinders.get(c)); + mBinders.remove(c); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Get a handover request message for use in WFA NFC Handover transfer. * @hide */ |