From 9e403fa05ae3b1c20811ab3d0b12e28e3ab25d93 Mon Sep 17 00:00:00 2001 From: Felipe Leme Date: Thu, 30 Nov 2017 17:41:57 -0800 Subject: Make sure apps cannot forge package name on AssistStructure used for Autofill. Test: cts-tradefed run commandAndExit cts-dev -m CtsAutoFillServiceTestCases -t android.autofillservice.cts.VirtualContainerActivityTest#testAppCannotFakePackageName Test: cts-tradefed run commandAndExit cts-dev -m CtsAutoFillServiceTestCases Bug: 69981710 Change-Id: Id6036cddb51dd8dd0c9128b7212d573f630d693f Merged-In: Id6036cddb51dd8dd0c9128b7212d573f630d693f (cherry picked from commit 23e61a9086a34405e277868474e003b37ed1b711) --- core/java/android/app/assist/AssistStructure.java | 10 ++++++ .../android/view/autofill/AutofillManager.java | 26 ++++++++++++-- .../android/view/autofill/IAutoFillManager.aidl | 6 ++-- proto/src/metrics_constants.proto | 13 +++++++ .../server/autofill/AutofillManagerService.java | 14 ++++---- .../autofill/AutofillManagerServiceImpl.java | 40 +++++++++++++++++++--- .../java/com/android/server/autofill/Session.java | 32 +++++++++++------ 7 files changed, 115 insertions(+), 26 deletions(-) diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 9383626360dc..bf715c35d9b7 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -2058,6 +2058,16 @@ public class AssistStructure implements Parcelable { return mActivityComponent; } + /** + * Called by Autofill server when app forged a different value. + * + * @hide + */ + public void setActivityComponent(ComponentName componentName) { + ensureData(); + mActivityComponent = componentName; + } + /** @hide */ public int getFlags() { return mFlags; diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 4fb2a99af575..ba738b6dfcea 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -24,6 +24,8 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemService; +import android.app.Activity; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentSender; @@ -44,6 +46,7 @@ import android.view.View; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.util.Preconditions; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -390,7 +393,7 @@ public final class AutofillManager { * @hide */ public AutofillManager(Context context, IAutoFillManager service) { - mContext = context; + mContext = Preconditions.checkNotNull(context, "context cannot be null"); mService = service; } @@ -940,6 +943,13 @@ public final class AutofillManager { return mContext.getAutofillClient(); } + private ComponentName getComponentNameFromContext() { + if (mContext instanceof Activity) { + return ((Activity) mContext).getComponentName(); + } + return null; + } + /** @hide */ public void onAuthenticationResult(int authenticationId, Intent data) { if (!hasAutofillFeature()) { @@ -990,9 +1000,14 @@ public final class AutofillManager { return; } try { + final ComponentName componentName = getComponentNameFromContext(); + if (componentName == null) { + Log.w(TAG, "startSessionLocked(): context is not activity: " + mContext); + return; + } mSessionId = mService.startSession(mContext.getActivityToken(), mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(), - mCallback != null, flags, mContext.getOpPackageName()); + mCallback != null, flags, componentName); if (mSessionId != NO_SESSION) { mState = STATE_ACTIVE; } @@ -1050,9 +1065,14 @@ public final class AutofillManager { try { if (restartIfNecessary) { + final ComponentName componentName = getComponentNameFromContext(); + if (componentName == null) { + Log.w(TAG, "startSessionLocked(): context is not activity: " + mContext); + return; + } final int newId = mService.updateOrRestartSession(mContext.getActivityToken(), mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(), - mCallback != null, flags, mContext.getOpPackageName(), mSessionId, action); + mCallback != null, flags, componentName, mSessionId, action); if (newId != mSessionId) { if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId); mSessionId = newId; diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl index 6bd9bec368c8..9329c4dcff6a 100644 --- a/core/java/android/view/autofill/IAutoFillManager.aidl +++ b/core/java/android/view/autofill/IAutoFillManager.aidl @@ -16,6 +16,7 @@ package android.view.autofill; +import android.content.ComponentName; import android.graphics.Rect; import android.os.Bundle; import android.os.IBinder; @@ -34,14 +35,15 @@ interface IAutoFillManager { int addClient(in IAutoFillManagerClient client, int userId); int startSession(IBinder activityToken, in IBinder appCallback, in AutofillId autoFillId, in Rect bounds, in AutofillValue value, int userId, boolean hasCallback, int flags, - String packageName); + in ComponentName componentName); FillEventHistory getFillEventHistory(); boolean restoreSession(int sessionId, in IBinder activityToken, in IBinder appCallback); void updateSession(int sessionId, in AutofillId id, in Rect bounds, in AutofillValue value, int action, int flags, int userId); int updateOrRestartSession(IBinder activityToken, in IBinder appCallback, in AutofillId autoFillId, in Rect bounds, in AutofillValue value, int userId, - boolean hasCallback, int flags, String packageName, int sessionId, int action); + boolean hasCallback, int flags, in ComponentName componentName, int sessionId, + int action); void finishSession(int sessionId, int userId); void cancelSession(int sessionId, int userId); void setAuthenticationResult(in Bundle data, int sessionId, int authenticationId, int userId); diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index a27515c0266d..3cadff242e03 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -4006,6 +4006,19 @@ message MetricsEvent { // OS: O FIELD_NOTIFICATION_GROUP_SUMMARY = 947; + // An app attempted to forge a different component name in the AssisStructure that would be + // passed to the autofill service. + // OS: O (security patch) + // Package: Real package of the app being autofilled + // Tag FIELD_AUTOFILL_SERVICE: Package of the autofill service that processed the request + // TAG FIELD_AUTOFILL_FORGED_COMPONENT_NAME: Component name being forged + AUTOFILL_FORGED_COMPONENT_ATTEMPT = 948; + + // FIELD - The component that an app tried tro forged. + // Type: string + // OS: O (security patch) + FIELD_AUTOFILL_FORGED_COMPONENT_NAME = 949; + // ---- End O Constants, all O constants go above this line ---- // OPEN: Settings > System > Languages & input > Advanced > Lift to open camera diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index 1f4161ac54d4..ffc778a8ee40 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -533,25 +533,26 @@ public final class AutofillManagerService extends SystemService { @Override public int startSession(IBinder activityToken, IBinder appCallback, AutofillId autofillId, Rect bounds, AutofillValue value, int userId, boolean hasCallback, int flags, - String packageName) { + ComponentName componentName) { activityToken = Preconditions.checkNotNull(activityToken, "activityToken"); appCallback = Preconditions.checkNotNull(appCallback, "appCallback"); autofillId = Preconditions.checkNotNull(autofillId, "autoFillId"); - packageName = Preconditions.checkNotNull(packageName, "packageName"); + componentName = Preconditions.checkNotNull(componentName, "componentName"); + final String packageName = Preconditions.checkNotNull(componentName.getPackageName()); Preconditions.checkArgument(userId == UserHandle.getUserId(getCallingUid()), "userId"); try { mContext.getPackageManager().getPackageInfoAsUser(packageName, 0, userId); } catch (PackageManager.NameNotFoundException e) { - throw new IllegalArgumentException(packageName + " is not a valid package", e); + throw new IllegalArgumentException(componentName + " is not a valid package", e); } synchronized (mLock) { final AutofillManagerServiceImpl service = getServiceForUserLocked(userId); return service.startSessionLocked(activityToken, getCallingUid(), appCallback, - autofillId, bounds, value, hasCallback, flags, packageName); + autofillId, bounds, value, hasCallback, flags, componentName); } } @@ -603,7 +604,8 @@ public final class AutofillManagerService extends SystemService { @Override public int updateOrRestartSession(IBinder activityToken, IBinder appCallback, AutofillId autoFillId, Rect bounds, AutofillValue value, int userId, - boolean hasCallback, int flags, String packageName, int sessionId, int action) { + boolean hasCallback, int flags, ComponentName componentName, int sessionId, + int action) { boolean restart = false; synchronized (mLock) { final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); @@ -614,7 +616,7 @@ public final class AutofillManagerService extends SystemService { } if (restart) { return startSession(activityToken, appCallback, autoFillId, bounds, value, userId, - hasCallback, flags, packageName); + hasCallback, flags, componentName); } // Nothing changed... diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 3a3b5707fc6f..a17c3ca92e5d 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -32,6 +32,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ServiceInfo; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -43,6 +44,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.service.autofill.AutofillService; @@ -279,7 +281,7 @@ final class AutofillManagerServiceImpl { int startSessionLocked(@NonNull IBinder activityToken, int uid, @NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId, @NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback, - int flags, @NonNull String packageName) { + int flags, @NonNull ComponentName componentName) { if (!isEnabled()) { return 0; } @@ -289,7 +291,7 @@ final class AutofillManagerServiceImpl { pruneAbandonedSessionsLocked(); final Session newSession = createSessionByTokenLocked(activityToken, uid, appCallbackToken, - hasCallback, packageName); + hasCallback, componentName); if (newSession == null) { return NO_SESSION; } @@ -386,7 +388,8 @@ final class AutofillManagerServiceImpl { } private Session createSessionByTokenLocked(@NonNull IBinder activityToken, int uid, - @NonNull IBinder appCallbackToken, boolean hasCallback, @NonNull String packageName) { + @NonNull IBinder appCallbackToken, boolean hasCallback, + @NonNull ComponentName componentName) { // use random ids so that one app cannot know that another app creates sessions int sessionId; int tries = 0; @@ -400,14 +403,43 @@ final class AutofillManagerServiceImpl { sessionId = sRandom.nextInt(); } while (sessionId == NO_SESSION || mSessions.indexOfKey(sessionId) >= 0); + assertCallerLocked(componentName); + final Session newSession = new Session(this, mUi, mContext, mHandlerCaller, mUserId, mLock, sessionId, uid, activityToken, appCallbackToken, hasCallback, - mUiLatencyHistory, mInfo.getServiceInfo().getComponentName(), packageName); + mUiLatencyHistory, mInfo.getServiceInfo().getComponentName(), componentName); mSessions.put(newSession.id, newSession); return newSession; } + /** + * Asserts the component is owned by the caller. + */ + private void assertCallerLocked(@NonNull ComponentName componentName) { + final PackageManager pm = mContext.getPackageManager(); + final int callingUid = Binder.getCallingUid(); + final int packageUid; + try { + packageUid = pm.getPackageUidAsUser(componentName.getPackageName(), + UserHandle.getCallingUserId()); + } catch (NameNotFoundException e) { + throw new SecurityException("Could not verify UID for " + componentName); + } + if (callingUid != packageUid) { + final String[] packages = pm.getPackagesForUid(callingUid); + final String callingPackage = packages != null ? packages[0] : "uid-" + callingUid; + Slog.w(TAG, "App (package=" + callingPackage + ", UID=" + callingUid + + ") passed component (" + componentName + ") owned by UID " + packageUid); + mMetricsLogger.write(new LogMaker(MetricsEvent.AUTOFILL_FORGED_COMPONENT_ATTEMPT) + .setPackageName(callingPackage) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, getServicePackageName()) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FORGED_COMPONENT_NAME, + componentName == null ? "null" : componentName.flattenToShortString())); + throw new SecurityException("Invalid component: " + componentName); + } + } + /** * Restores a session after an activity was temporarily destroyed. * diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index de2950dd5985..1f0e51c694aa 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -126,8 +126,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") @NonNull private IBinder mActivityToken; - /** Package name of the app that is auto-filled */ - @NonNull private final String mPackageName; + /** Component that's being auto-filled */ + @NonNull private final ComponentName mComponentName; @GuardedBy("mLock") private final ArrayMap mViewStates = new ArrayMap<>(); @@ -227,6 +227,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState structure.ensureData(); // Sanitize structure before it's sent to service. + final ComponentName componentNameFromApp = structure.getActivityComponent(); + if (!mComponentName.equals(componentNameFromApp)) { + Slog.w(TAG, "Activity " + mComponentName + " forged different component on " + + "AssistStructure: " + componentNameFromApp); + structure.setActivityComponent(mComponentName); + mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_FORGED_COMPONENT_ATTEMPT) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FORGED_COMPONENT_NAME, + componentNameFromApp == null ? "null" + : componentNameFromApp.flattenToShortString())); + } structure.sanitizeForParceling(true); // Flags used to start the session. @@ -415,7 +425,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @NonNull Context context, @NonNull HandlerCaller handlerCaller, int userId, @NonNull Object lock, int sessionId, int uid, @NonNull IBinder activityToken, @NonNull IBinder client, boolean hasCallback, @NonNull LocalLog uiLatencyHistory, - @NonNull ComponentName componentName, @NonNull String packageName) { + @NonNull ComponentName serviceComponentName, @NonNull ComponentName appComponentName) { id = sessionId; this.uid = uid; mStartTime = SystemClock.elapsedRealtime(); @@ -423,11 +433,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mLock = lock; mUi = ui; mHandlerCaller = handlerCaller; - mRemoteFillService = new RemoteFillService(context, componentName, userId, this); + mRemoteFillService = new RemoteFillService(context, serviceComponentName, userId, this); mActivityToken = activityToken; mHasCallback = hasCallback; mUiLatencyHistory = uiLatencyHistory; - mPackageName = packageName; + mComponentName = appComponentName; mClient = IAutoFillManagerClient.Stub.asInterface(client); writeLog(MetricsEvent.AUTOFILL_SESSION_STARTED); @@ -1008,8 +1018,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final IAutoFillManagerClient client = getClient(); mPendingSaveUi = new PendingUi(mActivityToken, id, client); getUiForShowing().showSaveUi(mService.getServiceLabel(), mService.getServiceIcon(), - mService.getServicePackageName(), saveInfo, valueFinder, mPackageName, this, - mPendingSaveUi); + mService.getServicePackageName(), saveInfo, valueFinder, + mComponentName.getPackageName(), this, mPendingSaveUi); if (client != null) { try { client.setSaveUiState(id, true); @@ -1365,7 +1375,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } getUiForShowing().showFillUi(filledId, response, filterText, - mService.getServicePackageName(), mPackageName, this); + mService.getServicePackageName(), mComponentName.getPackageName(), this); synchronized (mLock) { if (mUiShownTime == 0) { @@ -1690,14 +1700,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Override public String toString() { - return "Session: [id=" + id + ", pkg=" + mPackageName + "]"; + return "Session: [id=" + id + ", pkg=" + mComponentName.getPackageName() + "]"; } 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("mComponentName: "); pw.println(mComponentName); pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken); pw.print(prefix); pw.print("mStartTime: "); pw.println(mStartTime); pw.print(prefix); pw.print("Time to show UI: "); @@ -1920,7 +1930,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } private LogMaker newLogMaker(int category, String servicePackageName) { - return Helper.newLogMaker(category, mPackageName, servicePackageName); + return Helper.newLogMaker(category, mComponentName.getPackageName(), servicePackageName); } private void writeLog(int category) { -- cgit v1.2.3 From bdfe972294dd6c1ba2fbfb071c6b943de03cef50 Mon Sep 17 00:00:00 2001 From: Felipe Leme Date: Mon, 11 Dec 2017 14:37:35 -0800 Subject: Proper autofill fix to let phone process autofill Settings activity. Test: adb shell am start com.android.settings/.RadioInfo Bug: 69981710 Fixes: 70506888 Change-Id: Id29bad2d20b621f7379eb6144c95dcc819949b3d Merged-In: Id29bad2d20b621f7379eb6144c95dcc819949b3d (cherry picked from commit 97f16a76db29269619d9a1b45d4cea49026a5b6a) (cherry picked from commit 92b5d2783a1b97bee476f04754481403839b4e45) --- core/java/android/app/ActivityManagerInternal.java | 5 +++++ .../server/autofill/AutofillManagerServiceImpl.java | 9 ++++++--- .../android/server/am/ActivityManagerService.java | 20 ++++++++++++++++++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index c8d983933fc6..9dceb7f9e433 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -268,4 +268,9 @@ public abstract class ActivityManagerInternal { * @param token The IApplicationToken for the activity */ public abstract void setFocusedActivity(IBinder token); + + /** + * Returns {@code true} if {@code uid} is running an activity from {@code packageName}. + */ + public abstract boolean hasRunningActivity(int uid, @Nullable String packageName); } diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index a17c3ca92e5d..6d3398ea2648 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -26,6 +26,7 @@ import static com.android.server.autofill.Helper.sVerbose; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityManagerInternal; import android.app.AppGlobals; import android.app.IActivityManager; import android.content.ComponentName; @@ -69,6 +70,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.HandlerCaller; +import com.android.server.LocalServices; import com.android.server.autofill.ui.AutoFillUI; import java.io.PrintWriter; @@ -417,16 +419,17 @@ final class AutofillManagerServiceImpl { * Asserts the component is owned by the caller. */ private void assertCallerLocked(@NonNull ComponentName componentName) { + final String packageName = componentName.getPackageName(); final PackageManager pm = mContext.getPackageManager(); final int callingUid = Binder.getCallingUid(); final int packageUid; try { - packageUid = pm.getPackageUidAsUser(componentName.getPackageName(), - UserHandle.getCallingUserId()); + packageUid = pm.getPackageUidAsUser(packageName, UserHandle.getCallingUserId()); } catch (NameNotFoundException e) { throw new SecurityException("Could not verify UID for " + componentName); } - if (callingUid != packageUid) { + if (callingUid != packageUid && !LocalServices.getService(ActivityManagerInternal.class) + .hasRunningActivity(callingUid, packageName)) { final String[] packages = pm.getPackagesForUid(callingUid); final String callingPackage = packages != null ? packages[0] : "uid-" + callingUid; Slog.w(TAG, "App (package=" + callingPackage + ", UID=" + callingUid diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 1520b96a4afd..4a2d9c9ca5a4 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -24248,6 +24248,26 @@ public class ActivityManagerService extends IActivityManager.Stub } } } + + @Override + public boolean hasRunningActivity(int uid, @Nullable String packageName) { + if (packageName == null) return false; + + synchronized (ActivityManagerService.this) { + for (int i = 0; i < mLruProcesses.size(); i++) { + final ProcessRecord processRecord = mLruProcesses.get(i); + if (processRecord.uid == uid) { + for (int j = 0; j < processRecord.activities.size(); j++) { + final ActivityRecord activityRecord = processRecord.activities.get(j); + if (packageName.equals(activityRecord.packageName)) { + return true; + } + } + } + } + } + return false; + } } /** -- cgit v1.2.3 From fbbc3fb97bd03dea44ab1848a495e8e7aa4d3b4d Mon Sep 17 00:00:00 2001 From: Felipe Leme Date: Mon, 22 Jan 2018 15:33:40 -0800 Subject: DO NOT MERGE - fix AFM.getComponentNameFromContext() This method broke on O-MR1 when I3abf999eb6056c1df7982780bae43b58337c0668 was chery-picked from master. Test: cts-tradefed run commandAndExit cts-dev -m CtsAutoFillServiceTestCases \ -t android.autofillservice.cts.AttachedContextActivityTest#testAutofill Test: cts-tradefed run commandAndExit cts-dev -m CtsAutoFillServiceTestCases Also individually ran tests that failed (due to flakiness) when ran in a suite: Test: cts-tradefed run commandAndExit cts-dev -m CtsAutoFillServiceTestCases \ -t android.autofillservice.cts.OptionalSaveActivityTest#testDontShowSaveUiWhenUserManuallyFilled_oneDatasetAllRequiredFields Test: cts-tradefed run commandAndExit cts-dev -m CtsAutoFillServiceTestCases -t android.autofillservice.cts.PreSimpleSaveActivityTest #testTapLink_tapBack_thenStartOverBySayingYesAndManualRequest Fixes: 71960322 Change-Id: Ia093dcefe6699dc9493c46d671e48c2000214b31 Merged-In: I3abf999eb6056c1df7982780bae43b58337c0668 (cherry picked from commit b25b4736db2ea05dce6b9f03e8a9920ab338dd5a) --- core/java/android/app/Activity.java | 10 ++++++++++ .../android/view/autofill/AutofillManager.java | 23 ++++++++++++---------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 2c04f8ff9548..8dc558ccf755 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -5871,6 +5871,16 @@ public class Activity extends ContextThemeWrapper return mComponent; } + /** + * Temporary method on O-MR1 only. + * + * @hide + */ + @Override + public ComponentName getComponentNameForAutofill() { + return mComponent; + } + /** * Retrieve a {@link SharedPreferences} object for accessing preferences * that are private to this activity. This simply calls the underlying diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index ba738b6dfcea..fb9534b2e52e 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -24,7 +24,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemService; -import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -387,6 +386,13 @@ public final class AutofillManager { * Runs the specified action on the UI thread. */ void runOnUiThread(Runnable action); + + /** + * Gets the complete component name of this client. + * + *

Temporary method on O-MR1 only. + */ + ComponentName getComponentNameForAutofill(); } /** @@ -943,11 +949,8 @@ public final class AutofillManager { return mContext.getAutofillClient(); } - private ComponentName getComponentNameFromContext() { - if (mContext instanceof Activity) { - return ((Activity) mContext).getComponentName(); - } - return null; + private ComponentName getComponentNameFromContext(AutofillClient client) { + return client == null ? null : client.getComponentNameForAutofill(); } /** @hide */ @@ -1000,7 +1003,8 @@ public final class AutofillManager { return; } try { - final ComponentName componentName = getComponentNameFromContext(); + final AutofillClient client = getClientLocked(); + final ComponentName componentName = getComponentNameFromContext(client); if (componentName == null) { Log.w(TAG, "startSessionLocked(): context is not activity: " + mContext); return; @@ -1011,7 +1015,6 @@ public final class AutofillManager { if (mSessionId != NO_SESSION) { mState = STATE_ACTIVE; } - final AutofillClient client = getClientLocked(); if (client != null) { client.autofillCallbackResetableStateAvailable(); } @@ -1065,7 +1068,8 @@ public final class AutofillManager { try { if (restartIfNecessary) { - final ComponentName componentName = getComponentNameFromContext(); + final AutofillClient client = getClientLocked(); + final ComponentName componentName = getComponentNameFromContext(client); if (componentName == null) { Log.w(TAG, "startSessionLocked(): context is not activity: " + mContext); return; @@ -1077,7 +1081,6 @@ public final class AutofillManager { if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId); mSessionId = newId; mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE; - final AutofillClient client = getClientLocked(); if (client != null) { client.autofillCallbackResetableStateAvailable(); } -- cgit v1.2.3 From 4f2587a287226bfd7d1e564062a4869c9ff8720b Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 7 Nov 2017 13:52:02 -0800 Subject: Rework thumbnail cleanup Bug: 63766886 Test: ran CTS tests Change-Id: I1f92bb014e275eafe3f42aef1f8c817f187c6608 (cherry picked from commit 6d2096f3889d38da60099b1b5678347de4f042bf) --- core/java/android/provider/MediaStore.java | 4 +-- media/java/android/media/MediaScanner.java | 52 --------------------------- media/java/android/media/MiniThumbFile.java | 54 +++++++++++++++++++++++++++-- 3 files changed, 54 insertions(+), 56 deletions(-) diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 13e1e26b51c3..ff4f358529a4 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -694,8 +694,8 @@ public final class MediaStore { // Log.v(TAG, "getThumbnail: origId="+origId+", kind="+kind+", isVideo="+isVideo); // If the magic is non-zero, we simply return thumbnail if it does exist. // querying MediaProvider and simply return thumbnail. - MiniThumbFile thumbFile = new MiniThumbFile(isVideo ? Video.Media.EXTERNAL_CONTENT_URI - : Images.Media.EXTERNAL_CONTENT_URI); + MiniThumbFile thumbFile = MiniThumbFile.instance( + isVideo ? Video.Media.EXTERNAL_CONTENT_URI : Images.Media.EXTERNAL_CONTENT_URI); Cursor c = null; try { long magic = thumbFile.getMagic(origId); diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index cb4e46fe945a..e66945bd7ec4 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -323,7 +323,6 @@ public class MediaScanner implements AutoCloseable { private final Uri mAudioUri; private final Uri mVideoUri; private final Uri mImagesUri; - private final Uri mThumbsUri; private final Uri mPlaylistsUri; private final Uri mFilesUri; private final Uri mFilesUriNoNotify; @@ -419,7 +418,6 @@ public class MediaScanner implements AutoCloseable { mAudioUri = Audio.Media.getContentUri(volumeName); mVideoUri = Video.Media.getContentUri(volumeName); mImagesUri = Images.Media.getContentUri(volumeName); - mThumbsUri = Images.Thumbnails.getContentUri(volumeName); mFilesUri = Files.getContentUri(volumeName); mFilesUriNoNotify = mFilesUri.buildUpon().appendQueryParameter("nonotify", "1").build(); @@ -1283,53 +1281,6 @@ public class MediaScanner implements AutoCloseable { } } - private void pruneDeadThumbnailFiles() { - HashSet existingFiles = new HashSet(); - String directory = "/sdcard/DCIM/.thumbnails"; - String [] files = (new File(directory)).list(); - Cursor c = null; - if (files == null) - files = new String[0]; - - for (int i = 0; i < files.length; i++) { - String fullPathString = directory + "/" + files[i]; - existingFiles.add(fullPathString); - } - - try { - c = mMediaProvider.query( - mThumbsUri, - new String [] { "_data" }, - null, - null, - null, null); - Log.v(TAG, "pruneDeadThumbnailFiles... " + c); - if (c != null && c.moveToFirst()) { - do { - String fullPathString = c.getString(0); - existingFiles.remove(fullPathString); - } while (c.moveToNext()); - } - - for (String fileToDelete : existingFiles) { - if (false) - Log.v(TAG, "fileToDelete is " + fileToDelete); - try { - (new File(fileToDelete)).delete(); - } catch (SecurityException ex) { - } - } - - Log.v(TAG, "/pruneDeadThumbnailFiles... " + c); - } catch (RemoteException e) { - // We will soon be killed... - } finally { - if (c != null) { - c.close(); - } - } - } - static class MediaBulkDeleter { StringBuilder whereClause = new StringBuilder(); ArrayList whereArgs = new ArrayList(100); @@ -1373,9 +1324,6 @@ public class MediaScanner implements AutoCloseable { processPlayLists(); } - if (mOriginalCount == 0 && mImagesUri.equals(Images.Media.getContentUri("external"))) - pruneDeadThumbnailFiles(); - // allow GC to clean up mPlayLists.clear(); } diff --git a/media/java/android/media/MiniThumbFile.java b/media/java/android/media/MiniThumbFile.java index 664308c45bf9..98993676ce43 100644 --- a/media/java/android/media/MiniThumbFile.java +++ b/media/java/android/media/MiniThumbFile.java @@ -44,13 +44,14 @@ import java.util.Hashtable; */ public class MiniThumbFile { private static final String TAG = "MiniThumbFile"; - private static final int MINI_THUMB_DATA_FILE_VERSION = 3; + private static final int MINI_THUMB_DATA_FILE_VERSION = 4; public static final int BYTES_PER_MINTHUMB = 10000; private static final int HEADER_SIZE = 1 + 8 + 4; private Uri mUri; private RandomAccessFile mMiniThumbFile; private FileChannel mChannel; private ByteBuffer mBuffer; + private ByteBuffer mEmptyBuffer; private static final Hashtable sThumbFiles = new Hashtable(); @@ -127,9 +128,10 @@ public class MiniThumbFile { return mMiniThumbFile; } - public MiniThumbFile(Uri uri) { + private MiniThumbFile(Uri uri) { mUri = uri; mBuffer = ByteBuffer.allocateDirect(BYTES_PER_MINTHUMB); + mEmptyBuffer = ByteBuffer.allocateDirect(BYTES_PER_MINTHUMB); } public synchronized void deactivate() { @@ -184,6 +186,54 @@ public class MiniThumbFile { return 0; } + public synchronized void eraseMiniThumb(long id) { + RandomAccessFile r = miniThumbDataFile(); + if (r != null) { + long pos = id * BYTES_PER_MINTHUMB; + FileLock lock = null; + try { + mBuffer.clear(); + mBuffer.limit(1 + 8); + + lock = mChannel.lock(pos, BYTES_PER_MINTHUMB, false); + // check that we can read the following 9 bytes + // (1 for the "status" and 8 for the long) + if (mChannel.read(mBuffer, pos) == 9) { + mBuffer.position(0); + if (mBuffer.get() == 1) { + long currentMagic = mBuffer.getLong(); + if (currentMagic == 0) { + // there is no thumbnail stored here + Log.i(TAG, "no thumbnail for id " + id); + return; + } + // zero out the thumbnail slot + // Log.v(TAG, "clearing slot " + id + ", magic " + currentMagic + // + " at offset " + pos); + mChannel.write(mEmptyBuffer, pos); + } + } else { + // Log.v(TAG, "No slot"); + } + } catch (IOException ex) { + Log.v(TAG, "Got exception checking file magic: ", ex); + } catch (RuntimeException ex) { + // Other NIO related exception like disk full, read only channel..etc + Log.e(TAG, "Got exception when reading magic, id = " + id + + ", disk full or mount read-only? " + ex.getClass()); + } finally { + try { + if (lock != null) lock.release(); + } + catch (IOException ex) { + // ignore it. + } + } + } else { + // Log.v(TAG, "No data file"); + } + } + public synchronized void saveMiniThumbToFile(byte[] data, long id, long magic) throws IOException { RandomAccessFile r = miniThumbDataFile(); -- cgit v1.2.3 From c1db43a6440d9d6ff61248394603052b1ceb1a0a Mon Sep 17 00:00:00 2001 From: Fyodor Kupolov Date: Fri, 16 Mar 2018 12:20:40 -0700 Subject: Use concrete CREATOR instance for parceling lists Replaced readTypedArrayList/writeTypedArrayList with writeTypedList/createTypedArrayList(CREATOR) Bug: 71508348 Test: CtsAutoFillServiceTestCases pass Merged-In: I2a8321023b40cc74b7026eb0fb32a9cc5f5543a9 Change-Id: Id17d02e40a4ae567bf2d74d2ea8ba4d8a943bdb7 (cherry picked from commit 4921986db76b1580bcb6ec8b2fd381d1364a6325) --- core/java/android/os/Parcel.java | 15 ++++++++------- core/java/android/service/autofill/Dataset.java | 9 +++++---- core/java/android/service/autofill/SaveRequest.java | 8 ++++---- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index fae9d5310f8e..c6d3860c6600 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -1340,6 +1340,13 @@ public final class Parcel { * @see Parcelable */ public final void writeTypedList(List val) { + writeTypedList(val, 0); + } + + /** + * @hide + */ + public void writeTypedList(List val, int parcelableFlags) { if (val == null) { writeInt(-1); return; @@ -1348,13 +1355,7 @@ public final class Parcel { int i=0; writeInt(N); while (i < N) { - T item = val.get(i); - if (item != null) { - writeInt(1); - item.writeToParcel(this, 0); - } else { - writeInt(0); - } + writeTypedObject(val.get(i), parcelableFlags); i++; } } diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java index 65b0efcbe032..21a3df89e0f8 100644 --- a/core/java/android/service/autofill/Dataset.java +++ b/core/java/android/service/autofill/Dataset.java @@ -316,8 +316,8 @@ public final class Dataset implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { parcel.writeParcelable(mPresentation, flags); - parcel.writeTypedArrayList(mFieldIds, flags); - parcel.writeTypedArrayList(mFieldValues, flags); + parcel.writeTypedList(mFieldIds, flags); + parcel.writeTypedList(mFieldValues, flags); parcel.writeParcelableList(mFieldPresentations, flags); parcel.writeParcelable(mAuthentication, flags); parcel.writeString(mId); @@ -333,8 +333,9 @@ public final class Dataset implements Parcelable { final Builder builder = (presentation == null) ? new Builder() : new Builder(presentation); - final ArrayList ids = parcel.readTypedArrayList(null); - final ArrayList values = parcel.readTypedArrayList(null); + final ArrayList ids = parcel.createTypedArrayList(AutofillId.CREATOR); + final ArrayList values = + parcel.createTypedArrayList(AutofillValue.CREATOR); final ArrayList presentations = new ArrayList<>(); parcel.readParcelableList(presentations, null); final int idCount = (ids != null) ? ids.size() : 0; diff --git a/core/java/android/service/autofill/SaveRequest.java b/core/java/android/service/autofill/SaveRequest.java index 9de931542cb9..fc4272dd7742 100644 --- a/core/java/android/service/autofill/SaveRequest.java +++ b/core/java/android/service/autofill/SaveRequest.java @@ -19,9 +19,9 @@ package android.service.autofill; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Bundle; -import android.os.CancellationSignal; import android.os.Parcel; import android.os.Parcelable; + import com.android.internal.util.Preconditions; import java.util.ArrayList; @@ -45,7 +45,7 @@ public final class SaveRequest implements Parcelable { } private SaveRequest(@NonNull Parcel parcel) { - this(parcel.readTypedArrayList(null), parcel.readBundle()); + this(parcel.createTypedArrayList(FillContext.CREATOR), parcel.readBundle()); } /** @@ -57,7 +57,7 @@ public final class SaveRequest implements Parcelable { /** * Gets the extra client state returned from the last {@link - * AutofillService#onFillRequest(FillRequest, CancellationSignal, FillCallback)} + * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)} * fill request}. * * @return The client state. @@ -73,7 +73,7 @@ public final class SaveRequest implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { - parcel.writeTypedArrayList(mFillContexts, flags); + parcel.writeTypedList(mFillContexts, flags); parcel.writeBundle(mClientState); } -- cgit v1.2.3 From 0da68f30a9262a74c47a6c12f99d848320c8aefc Mon Sep 17 00:00:00 2001 From: akirilov Date: Tue, 27 Mar 2018 13:08:47 -0700 Subject: RESTRICT AUTOMERGE: Prevent reporting fake package name - framework (backport to oc-mr1-dev) Test: added AccessibilityEndToEndTest#testPackageNameCannotBeFaked cts-tradefed run cts -m CtsAccessibilityServiceTestCases cts-tradefed run cts -m CtsAccessibilityTestCases Bug: 69981755 Change-Id: If3752e106aa7fdee4645dc9852289af471ceff18 Merged-In: I13304efbee10d1affa087e9c8bc4ec237643283e (cherry picked from commit c36db6d473c9988496cd614924ee113b67f7e333) --- .../IAccessibilityServiceConnection.aidl | 10 +- .../appwidget/AppWidgetManagerInternal.java | 36 +++ core/java/android/view/ViewRootImpl.java | 1 + .../AccessibilityInteractionClient.java | 115 +++++--- .../view/accessibility/AccessibilityManager.java | 5 +- .../view/accessibility/IAccessibilityManager.aidl | 2 +- .../accessibility/AccessibilityManagerService.java | 288 +++++++++++++++------ .../server/appwidget/AppWidgetServiceImpl.java | 25 ++ 8 files changed, 360 insertions(+), 122 deletions(-) create mode 100644 core/java/android/appwidget/AppWidgetManagerInternal.java diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index 7a1931718888..037aeb058f15 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -35,23 +35,23 @@ interface IAccessibilityServiceConnection { void setServiceInfo(in AccessibilityServiceInfo info); - boolean findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId, + String[] findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId, long accessibilityNodeId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, long threadId, in Bundle arguments); - boolean findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId, + String[] findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId, String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId); - boolean findAccessibilityNodeInfosByViewId(int accessibilityWindowId, + String[] findAccessibilityNodeInfosByViewId(int accessibilityWindowId, long accessibilityNodeId, String viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId); - boolean findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType, + String[] findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType, int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId); - boolean focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction, + String[] focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction, int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId); boolean performAccessibilityAction(int accessibilityWindowId, long accessibilityNodeId, diff --git a/core/java/android/appwidget/AppWidgetManagerInternal.java b/core/java/android/appwidget/AppWidgetManagerInternal.java new file mode 100644 index 000000000000..5562c550fe18 --- /dev/null +++ b/core/java/android/appwidget/AppWidgetManagerInternal.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 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.appwidget; + +import android.annotation.Nullable; +import android.util.ArraySet; + +/** + * App widget manager local system service interface. + * + * @hide Only for use within the system server. + */ +public abstract class AppWidgetManagerInternal { + + /** + * Gets the packages from which the uid hosts widgets. + * + * @param uid The potential host UID. + * @return Whether the UID hosts widgets from the package. + */ + public abstract @Nullable ArraySet getHostedWidgetPackages(int uid); +} diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 8f250a9e9f15..3f1ea34c37ab 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -7733,6 +7733,7 @@ public final class ViewRootImpl implements ViewParent, if (!registered) { mAttachInfo.mAccessibilityWindowId = mAccessibilityManager.addAccessibilityInteractionConnection(mWindow, + mContext.getPackageName(), new AccessibilityInteractionConnection(ViewRootImpl.this)); } } diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index 19213ca06c5e..be3b34d0ccf8 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -28,6 +28,8 @@ import android.util.Log; import android.util.LongSparseArray; import android.util.SparseArray; +import com.android.internal.util.ArrayUtils; + import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -283,14 +285,19 @@ public final class AccessibilityInteractionClient } final int interactionId = mInteractionIdCounter.getAndIncrement(); final long identityToken = Binder.clearCallingIdentity(); - final boolean success = connection.findAccessibilityNodeInfoByAccessibilityId( - accessibilityWindowId, accessibilityNodeId, interactionId, this, - prefetchFlags, Thread.currentThread().getId(), arguments); - Binder.restoreCallingIdentity(identityToken); - if (success) { + final String[] packageNames; + try { + packageNames = connection.findAccessibilityNodeInfoByAccessibilityId( + accessibilityWindowId, accessibilityNodeId, interactionId, this, + prefetchFlags, Thread.currentThread().getId(), arguments); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + if (packageNames != null) { List infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); - finalizeAndCacheAccessibilityNodeInfos(infos, connectionId); + finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, + bypassCache, packageNames); if (infos != null && !infos.isEmpty()) { for (int i = 1; i < infos.size(); i++) { infos.get(i).recycle(); @@ -333,15 +340,21 @@ public final class AccessibilityInteractionClient if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); final long identityToken = Binder.clearCallingIdentity(); - final boolean success = connection.findAccessibilityNodeInfosByViewId( - accessibilityWindowId, accessibilityNodeId, viewId, interactionId, this, - Thread.currentThread().getId()); - Binder.restoreCallingIdentity(identityToken); - if (success) { + final String[] packageNames; + try { + packageNames = connection.findAccessibilityNodeInfosByViewId( + accessibilityWindowId, accessibilityNodeId, viewId, interactionId, this, + Thread.currentThread().getId()); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + + if (packageNames != null) { List infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); if (infos != null) { - finalizeAndCacheAccessibilityNodeInfos(infos, connectionId); + finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, + false, packageNames); return infos; } } @@ -381,15 +394,21 @@ public final class AccessibilityInteractionClient if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); final long identityToken = Binder.clearCallingIdentity(); - final boolean success = connection.findAccessibilityNodeInfosByText( - accessibilityWindowId, accessibilityNodeId, text, interactionId, this, - Thread.currentThread().getId()); - Binder.restoreCallingIdentity(identityToken); - if (success) { + final String[] packageNames; + try { + packageNames = connection.findAccessibilityNodeInfosByText( + accessibilityWindowId, accessibilityNodeId, text, interactionId, this, + Thread.currentThread().getId()); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + + if (packageNames != null) { List infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); if (infos != null) { - finalizeAndCacheAccessibilityNodeInfos(infos, connectionId); + finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, + false, packageNames); return infos; } } @@ -428,14 +447,19 @@ public final class AccessibilityInteractionClient if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); final long identityToken = Binder.clearCallingIdentity(); - final boolean success = connection.findFocus(accessibilityWindowId, - accessibilityNodeId, focusType, interactionId, this, - Thread.currentThread().getId()); - Binder.restoreCallingIdentity(identityToken); - if (success) { + final String[] packageNames; + try { + packageNames = connection.findFocus(accessibilityWindowId, + accessibilityNodeId, focusType, interactionId, this, + Thread.currentThread().getId()); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + + if (packageNames != null) { AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( interactionId); - finalizeAndCacheAccessibilityNodeInfo(info, connectionId); + finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false, packageNames); return info; } } else { @@ -472,14 +496,19 @@ public final class AccessibilityInteractionClient if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); final long identityToken = Binder.clearCallingIdentity(); - final boolean success = connection.focusSearch(accessibilityWindowId, - accessibilityNodeId, direction, interactionId, this, - Thread.currentThread().getId()); - Binder.restoreCallingIdentity(identityToken); - if (success) { + final String[] packageNames; + try { + packageNames = connection.focusSearch(accessibilityWindowId, + accessibilityNodeId, direction, interactionId, this, + Thread.currentThread().getId()); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + + if (packageNames != null) { AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( interactionId); - finalizeAndCacheAccessibilityNodeInfo(info, connectionId); + finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false, packageNames); return info; } } else { @@ -580,7 +609,7 @@ public final class AccessibilityInteractionClient int interactionId) { synchronized (mInstanceLock) { final boolean success = waitForResultTimedLocked(interactionId); - List result = null; + final List result; if (success) { result = mFindAccessibilityNodeInfosResult; } else { @@ -696,13 +725,25 @@ public final class AccessibilityInteractionClient * * @param info The info. * @param connectionId The id of the connection to the system. + * @param bypassCache Whether or not to bypass the cache. The node is added to the cache if + * this value is {@code false} + * @param packageNames The valid package names a node can come from. */ private void finalizeAndCacheAccessibilityNodeInfo(AccessibilityNodeInfo info, - int connectionId) { + int connectionId, boolean bypassCache, String[] packageNames) { if (info != null) { info.setConnectionId(connectionId); + // Empty array means any package name is Okay + if (!ArrayUtils.isEmpty(packageNames) + && !ArrayUtils.contains(packageNames, info.getPackageName().toString())) { + // If the node package not one of the valid ones, pick the top one - this + // is one of the packages running in the introspected UID. + info.setPackageName(packageNames[0]); + } info.setSealed(true); - sAccessibilityCache.add(info); + if (!bypassCache) { + sAccessibilityCache.add(info); + } } } @@ -711,14 +752,18 @@ public final class AccessibilityInteractionClient * * @param infos The {@link AccessibilityNodeInfo}s. * @param connectionId The id of the connection to the system. + * @param bypassCache Whether or not to bypass the cache. The nodes are added to the cache if + * this value is {@code false} + * @param packageNames The valid package names a node can come from. */ private void finalizeAndCacheAccessibilityNodeInfos(List infos, - int connectionId) { + int connectionId, boolean bypassCache, String[] packageNames) { if (infos != null) { final int infosCount = infos.size(); for (int i = 0; i < infosCount; i++) { AccessibilityNodeInfo info = infos.get(i); - finalizeAndCacheAccessibilityNodeInfo(info, connectionId); + finalizeAndCacheAccessibilityNodeInfo(info, connectionId, + bypassCache, packageNames); } } } diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index 0b9bc5760fa8..2478281efcd1 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -885,7 +885,7 @@ public final class AccessibilityManager { * @hide */ public int addAccessibilityInteractionConnection(IWindow windowToken, - IAccessibilityInteractionConnection connection) { + String packageName, IAccessibilityInteractionConnection connection) { final IAccessibilityManager service; final int userId; synchronized (mLock) { @@ -896,7 +896,8 @@ public final class AccessibilityManager { userId = mUserId; } try { - return service.addAccessibilityInteractionConnection(windowToken, connection, userId); + return service.addAccessibilityInteractionConnection(windowToken, connection, + packageName, userId); } catch (RemoteException re) { Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re); } diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl index 3f499abd2e4d..6329c1141bd5 100644 --- a/core/java/android/view/accessibility/IAccessibilityManager.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -45,7 +45,7 @@ interface IAccessibilityManager { List getEnabledAccessibilityServiceList(int feedbackType, int userId); int addAccessibilityInteractionConnection(IWindow windowToken, - in IAccessibilityInteractionConnection connection, int userId); + in IAccessibilityInteractionConnection connection, String packageName, int userId); void removeAccessibilityInteractionConnection(IWindow windowToken); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 83dfccb57ebf..d0d65d94fb5b 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -34,6 +34,7 @@ import android.app.AlertDialog; import android.app.PendingIntent; import android.app.StatusBarManager; import android.app.UiAutomation; +import android.appwidget.AppWidgetManagerInternal; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -82,6 +83,7 @@ import android.text.TextUtils.SimpleStringSplitter; import android.util.IntArray; import android.util.Slog; import android.util.SparseArray; +import android.util.ArraySet; import android.view.Display; import android.view.IWindow; import android.view.InputDevice; @@ -110,10 +112,11 @@ import com.android.internal.content.PackageMonitor; import com.android.internal.os.SomeArgs; import com.android.internal.util.DumpUtils; import com.android.internal.util.IntPair; +import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; import com.android.server.policy.AccessibilityShortcutController; import com.android.server.statusbar.StatusBarManagerInternal; - +import libcore.util.EmptyArray; import org.xmlpull.v1.XmlPullParserException; import java.io.FileDescriptor; @@ -199,6 +202,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private final WindowManagerInternal mWindowManagerService; + private AppWidgetManagerInternal mAppWidgetService; + private final SecurityPolicy mSecurityPolicy; private final MainHandler mMainHandler; @@ -229,10 +234,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private final RemoteCallbackList mGlobalClients = new RemoteCallbackList<>(); - private final SparseArray mGlobalInteractionConnections = + private final SparseArray mGlobalInteractionConnections = new SparseArray<>(); - private AccessibilityConnectionWrapper mPictureInPictureActionReplacingConnection; + private RemoteAccessibilityConnection mPictureInPictureActionReplacingConnection; private final SparseArray mGlobalWindowTokens = new SparseArray<>(); @@ -501,6 +506,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { // performs the current profile parent resolution.. final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked(userId); + + // Make sure the reported package is one the caller has access to. + event.setPackageName(mSecurityPolicy.resolveValidReportedPackageLocked( + event.getPackageName(), UserHandle.getCallingAppId(), resolvedUserId)); + // This method does nothing for a background user. if (resolvedUserId == mCurrentUserId) { if (mSecurityPolicy.canDispatchAccessibilityEventLocked(event)) { @@ -627,30 +637,38 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { @Override public int addAccessibilityInteractionConnection(IWindow windowToken, - IAccessibilityInteractionConnection connection, int userId) throws RemoteException { + IAccessibilityInteractionConnection connection, String packageName, + int userId) throws RemoteException { synchronized (mLock) { // We treat calls from a profile as if made by its parent as profiles // share the accessibility state of the parent. The call below // performs the current profile parent resolution. final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked(userId); + final int resolvedUid = UserHandle.getUid(resolvedUserId, UserHandle.getCallingAppId()); + + // Make sure the reported package is one the caller has access to. + packageName = mSecurityPolicy.resolveValidReportedPackageLocked( + packageName, UserHandle.getCallingAppId(), resolvedUserId); + final int windowId = sNextWindowId++; // If the window is from a process that runs across users such as // the system UI or the system we add it to the global state that // is shared across users. if (mSecurityPolicy.isCallerInteractingAcrossUsers(userId)) { - AccessibilityConnectionWrapper wrapper = new AccessibilityConnectionWrapper( - windowId, connection, UserHandle.USER_ALL); + RemoteAccessibilityConnection wrapper = new RemoteAccessibilityConnection( + windowId, connection, packageName, resolvedUid, UserHandle.USER_ALL); wrapper.linkToDeath(); mGlobalInteractionConnections.put(windowId, wrapper); mGlobalWindowTokens.put(windowId, windowToken.asBinder()); if (DEBUG) { Slog.i(LOG_TAG, "Added global connection for pid:" + Binder.getCallingPid() - + " with windowId: " + windowId + " and token: " + windowToken.asBinder()); + + " with windowId: " + windowId + " and token: " + + windowToken.asBinder()); } } else { - AccessibilityConnectionWrapper wrapper = new AccessibilityConnectionWrapper( - windowId, connection, resolvedUserId); + RemoteAccessibilityConnection wrapper = new RemoteAccessibilityConnection( + windowId, connection, packageName, resolvedUid, resolvedUserId); wrapper.linkToDeath(); UserState userState = getUserStateLocked(resolvedUserId); userState.mInteractionConnections.put(windowId, wrapper); @@ -679,7 +697,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { if (removedWindowId >= 0) { if (DEBUG) { Slog.i(LOG_TAG, "Removed global connection for pid:" + Binder.getCallingPid() - + " with windowId: " + removedWindowId + " and token: " + window.asBinder()); + + " with windowId: " + removedWindowId + " and token: " + + window.asBinder()); } return; } @@ -703,13 +722,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private int removeAccessibilityInteractionConnectionInternalLocked(IBinder windowToken, SparseArray windowTokens, - SparseArray interactionConnections) { + SparseArray interactionConnections) { final int count = windowTokens.size(); for (int i = 0; i < count; i++) { if (windowTokens.valueAt(i) == windowToken) { final int windowId = windowTokens.keyAt(i); windowTokens.removeAt(i); - AccessibilityConnectionWrapper wrapper = interactionConnections.get(windowId); + RemoteAccessibilityConnection wrapper = interactionConnections.get(windowId); wrapper.unlinkToDeath(); interactionConnections.remove(windowId); return windowId; @@ -729,9 +748,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { mPictureInPictureActionReplacingConnection = null; } if (connection != null) { - AccessibilityConnectionWrapper wrapper = new AccessibilityConnectionWrapper( + RemoteAccessibilityConnection wrapper = new RemoteAccessibilityConnection( AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID, - connection, UserHandle.USER_ALL); + connection, "foo.bar.baz", Process.SYSTEM_UID, UserHandle.USER_ALL); mPictureInPictureActionReplacingConnection = wrapper; wrapper.linkToDeath(); } @@ -2023,7 +2042,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final long identity = Binder.clearCallingIdentity(); try { Settings.Secure.putStringForUser(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, null, userState.mUserId); + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, null, + userState.mUserId); Settings.Secure.putIntForUser(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, 0, userState.mUserId); @@ -2354,18 +2374,35 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } - private class AccessibilityConnectionWrapper implements DeathRecipient { + class RemoteAccessibilityConnection implements DeathRecipient { + private final int mUid; + private final String mPackageName; private final int mWindowId; private final int mUserId; private final IAccessibilityInteractionConnection mConnection; - public AccessibilityConnectionWrapper(int windowId, - IAccessibilityInteractionConnection connection, int userId) { + RemoteAccessibilityConnection(int windowId, + IAccessibilityInteractionConnection connection, + String packageName, int uid, int userId) { mWindowId = windowId; + mPackageName = packageName; + mUid = uid; mUserId = userId; mConnection = connection; } + public int getUid() { + return mUid; + } + + public String getPackageName() { + return mPackageName; + } + + public IAccessibilityInteractionConnection getRemote() { + return mConnection; + } + public void linkToDeath() throws RemoteException { mConnection.asBinder().linkToDeath(this, 0); } @@ -3041,28 +3078,28 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } @Override - public boolean findAccessibilityNodeInfosByViewId(int accessibilityWindowId, + public String[] findAccessibilityNodeInfosByViewId(int accessibilityWindowId, long accessibilityNodeId, String viewIdResName, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException { final int resolvedWindowId; - IAccessibilityInteractionConnection connection = null; + RemoteAccessibilityConnection connection = null; Region partialInteractiveRegion = Region.obtain(); MagnificationSpec spec; synchronized (mLock) { mUsesAccessibilityCache = true; if (!isCalledForCurrentUserLocked()) { - return false; + return null; } resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); final boolean permissionGranted = mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); if (!permissionGranted) { - return false; + return null; } else { connection = getConnectionLocked(resolvedWindowId); if (connection == null) { - return false; + return null; } } if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked( @@ -3075,12 +3112,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final int interrogatingPid = Binder.getCallingPid(); callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, interrogatingPid, interrogatingTid); + final int callingUid = Binder.getCallingUid(); final long identityToken = Binder.clearCallingIdentity(); try { - connection.findAccessibilityNodeInfosByViewId(accessibilityNodeId, viewIdResName, - partialInteractiveRegion, interactionId, callback, mFetchFlags, - interrogatingPid, interrogatingTid, spec); - return true; + connection.getRemote().findAccessibilityNodeInfosByViewId(accessibilityNodeId, + viewIdResName, partialInteractiveRegion, interactionId, callback, + mFetchFlags, interrogatingPid, interrogatingTid, spec); + return mSecurityPolicy.computeValidReportedPackages(callingUid, + connection.getPackageName(), connection.getUid()); } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error findAccessibilityNodeInfoByViewId()."); @@ -3088,36 +3127,36 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } finally { Binder.restoreCallingIdentity(identityToken); // Recycle if passed to another process. - if (partialInteractiveRegion != null && Binder.isProxy(connection)) { + if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) { partialInteractiveRegion.recycle(); } } - return false; + return null; } @Override - public boolean findAccessibilityNodeInfosByText(int accessibilityWindowId, + public String[] findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId, String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException { final int resolvedWindowId; - IAccessibilityInteractionConnection connection = null; + RemoteAccessibilityConnection connection = null; Region partialInteractiveRegion = Region.obtain(); MagnificationSpec spec; synchronized (mLock) { mUsesAccessibilityCache = true; if (!isCalledForCurrentUserLocked()) { - return false; + return null; } resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); final boolean permissionGranted = mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); if (!permissionGranted) { - return false; + return null; } else { connection = getConnectionLocked(resolvedWindowId); if (connection == null) { - return false; + return null; } } if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked( @@ -3130,12 +3169,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final int interrogatingPid = Binder.getCallingPid(); callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, interrogatingPid, interrogatingTid); + final int callingUid = Binder.getCallingUid(); final long identityToken = Binder.clearCallingIdentity(); try { - connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text, + connection.getRemote().findAccessibilityNodeInfosByText(accessibilityNodeId, text, partialInteractiveRegion, interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid, spec); - return true; + return mSecurityPolicy.computeValidReportedPackages(callingUid, + connection.getPackageName(), connection.getUid()); } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfosByText()"); @@ -3143,36 +3184,36 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } finally { Binder.restoreCallingIdentity(identityToken); // Recycle if passed to another process. - if (partialInteractiveRegion != null && Binder.isProxy(connection)) { + if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) { partialInteractiveRegion.recycle(); } } - return false; + return null; } @Override - public boolean findAccessibilityNodeInfoByAccessibilityId( + public String[] findAccessibilityNodeInfoByAccessibilityId( int accessibilityWindowId, long accessibilityNodeId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, long interrogatingTid, Bundle arguments) throws RemoteException { final int resolvedWindowId; - IAccessibilityInteractionConnection connection = null; + RemoteAccessibilityConnection connection = null; Region partialInteractiveRegion = Region.obtain(); MagnificationSpec spec; synchronized (mLock) { mUsesAccessibilityCache = true; if (!isCalledForCurrentUserLocked()) { - return false; + return null; } resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); final boolean permissionGranted = mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); if (!permissionGranted) { - return false; + return null; } else { connection = getConnectionLocked(resolvedWindowId); if (connection == null) { - return false; + return null; } } if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked( @@ -3185,12 +3226,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final int interrogatingPid = Binder.getCallingPid(); callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, interrogatingPid, interrogatingTid); + final int callingUid = Binder.getCallingUid(); final long identityToken = Binder.clearCallingIdentity(); try { - connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId, - partialInteractiveRegion, interactionId, callback, mFetchFlags | flags, - interrogatingPid, interrogatingTid, spec, arguments); - return true; + connection.getRemote().findAccessibilityNodeInfoByAccessibilityId( + accessibilityNodeId, partialInteractiveRegion, interactionId, callback, + mFetchFlags | flags, interrogatingPid, interrogatingTid, spec, arguments); + return mSecurityPolicy.computeValidReportedPackages(callingUid, + connection.getPackageName(), connection.getUid()); } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()"); @@ -3198,36 +3241,36 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } finally { Binder.restoreCallingIdentity(identityToken); // Recycle if passed to another process. - if (partialInteractiveRegion != null && Binder.isProxy(connection)) { + if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) { partialInteractiveRegion.recycle(); } } - return false; + return null; } @Override - public boolean findFocus(int accessibilityWindowId, long accessibilityNodeId, + public String[] findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException { final int resolvedWindowId; - IAccessibilityInteractionConnection connection = null; + RemoteAccessibilityConnection connection = null; Region partialInteractiveRegion = Region.obtain(); MagnificationSpec spec; synchronized (mLock) { if (!isCalledForCurrentUserLocked()) { - return false; + return null; } resolvedWindowId = resolveAccessibilityWindowIdForFindFocusLocked( accessibilityWindowId, focusType); final boolean permissionGranted = mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); if (!permissionGranted) { - return false; + return null; } else { connection = getConnectionLocked(resolvedWindowId); if (connection == null) { - return false; + return null; } } if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked( @@ -3240,12 +3283,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final int interrogatingPid = Binder.getCallingPid(); callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, interrogatingPid, interrogatingTid); + final int callingUid = Binder.getCallingUid(); final long identityToken = Binder.clearCallingIdentity(); try { - connection.findFocus(accessibilityNodeId, focusType, partialInteractiveRegion, - interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid, - spec); - return true; + connection.getRemote().findFocus(accessibilityNodeId, focusType, + partialInteractiveRegion, interactionId, callback, mFetchFlags, + interrogatingPid, interrogatingTid, spec); + return mSecurityPolicy.computeValidReportedPackages(callingUid, + connection.getPackageName(), connection.getUid()); } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error calling findFocus()"); @@ -3253,35 +3298,35 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } finally { Binder.restoreCallingIdentity(identityToken); // Recycle if passed to another process. - if (partialInteractiveRegion != null && Binder.isProxy(connection)) { + if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) { partialInteractiveRegion.recycle(); } } - return false; + return null; } @Override - public boolean focusSearch(int accessibilityWindowId, long accessibilityNodeId, + public String[] focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException { final int resolvedWindowId; - IAccessibilityInteractionConnection connection = null; + RemoteAccessibilityConnection connection = null; Region partialInteractiveRegion = Region.obtain(); MagnificationSpec spec; synchronized (mLock) { if (!isCalledForCurrentUserLocked()) { - return false; + return null; } resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); final boolean permissionGranted = mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); if (!permissionGranted) { - return false; + return null; } else { connection = getConnectionLocked(resolvedWindowId); if (connection == null) { - return false; + return null; } } if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked( @@ -3294,12 +3339,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final int interrogatingPid = Binder.getCallingPid(); callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, interrogatingPid, interrogatingTid); + final int callingUid = Binder.getCallingUid(); final long identityToken = Binder.clearCallingIdentity(); try { - connection.focusSearch(accessibilityNodeId, direction, partialInteractiveRegion, - interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid, - spec); - return true; + connection.getRemote().focusSearch(accessibilityNodeId, direction, + partialInteractiveRegion, interactionId, callback, mFetchFlags, + interrogatingPid, interrogatingTid, spec); + return mSecurityPolicy.computeValidReportedPackages(callingUid, + connection.getPackageName(), connection.getUid()); } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error calling accessibilityFocusSearch()"); @@ -3307,11 +3354,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } finally { Binder.restoreCallingIdentity(identityToken); // Recycle if passed to another process. - if (partialInteractiveRegion != null && Binder.isProxy(connection)) { + if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) { partialInteractiveRegion.recycle(); } } - return false; + return null; } @Override @@ -3351,8 +3398,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException { final int resolvedWindowId; - IAccessibilityInteractionConnection connection = null; IBinder activityToken = null; + RemoteAccessibilityConnection connection; synchronized (mLock) { if (!isCalledForCurrentUserLocked()) { return false; @@ -3375,7 +3422,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { if ((a11yWindowInfo != null) && a11yWindowInfo.inPictureInPicture()) { if ((mPictureInPictureActionReplacingConnection != null) && !isA11yFocusAction) { - connection = mPictureInPictureActionReplacingConnection.mConnection; + connection = mPictureInPictureActionReplacingConnection; } } } @@ -3391,8 +3438,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { LocalServices.getService(ActivityManagerInternal.class) .setFocusedActivity(activityToken); } - connection.performAccessibilityAction(accessibilityNodeId, action, arguments, - interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid); + connection.mConnection.performAccessibilityAction(accessibilityNodeId, action, + arguments, interactionId, callback, mFetchFlags, interrogatingPid, + interrogatingTid); } catch (RemoteException re) { if (DEBUG) { Slog.e(LOG_TAG, "Error calling performAccessibilityAction()"); @@ -4072,16 +4120,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { LocalServices.getService(StatusBarManagerInternal.class).toggleSplitScreen(); } - private IAccessibilityInteractionConnection getConnectionLocked(int windowId) { + private RemoteAccessibilityConnection getConnectionLocked(int windowId) { if (DEBUG) { Slog.i(LOG_TAG, "Trying to get interaction connection to windowId: " + windowId); } - AccessibilityConnectionWrapper wrapper = mGlobalInteractionConnections.get(windowId); + RemoteAccessibilityConnection wrapper = mGlobalInteractionConnections.get(windowId); if (wrapper == null) { wrapper = getCurrentUserStateLocked().mInteractionConnections.get(windowId); } if (wrapper != null && wrapper.mConnection != null) { - return wrapper.mConnection; + return wrapper; } if (DEBUG) { Slog.e(LOG_TAG, "No interaction connection to window: " + windowId); @@ -4230,6 +4278,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } + private AppWidgetManagerInternal getAppWidgetManager() { + synchronized (mLock) { + if (mAppWidgetService == null + && mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) { + mAppWidgetService = LocalServices.getService(AppWidgetManagerInternal.class); + } + return mAppWidgetService; + } + } + final class WindowsForAccessibilityCallback implements WindowManagerInternal.WindowsForAccessibilityCallback { @@ -4507,6 +4565,78 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } + private boolean isValidPackageForUid(String packageName, int uid) { + try { + return uid == mPackageManager.getPackageUid( + packageName, UserHandle.getUserId(uid)); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + + String resolveValidReportedPackageLocked(CharSequence packageName, int appId, int userId) { + // Okay to pass no package + if (packageName == null) { + return null; + } + // The system gets to pass any package + if (appId == Process.SYSTEM_UID) { + return packageName.toString(); + } + // Passing a package in your UID is fine + final String packageNameStr = packageName.toString(); + final int resolvedUid = UserHandle.getUid(userId, appId); + if (isValidPackageForUid(packageNameStr, resolvedUid)) { + return packageName.toString(); + } + // Appwidget hosts get to pass packages for widgets they host + final AppWidgetManagerInternal appWidgetManager = getAppWidgetManager(); + if (appWidgetManager != null && ArrayUtils.contains(appWidgetManager + .getHostedWidgetPackages(resolvedUid), packageNameStr)) { + return packageName.toString(); + } + // Otherwise, set the package to the first one in the UID + final String[] packageNames = mPackageManager.getPackagesForUid(resolvedUid); + if (ArrayUtils.isEmpty(packageNames)) { + return null; + } + // Okay, the caller reported a package it does not have access to. + // Instead of crashing the caller for better backwards compatibility + // we report the first package in the UID. Since most of the time apps + // don't use shared user id, this will yield correct results and for + // the edge case of using a shared user id we may report the wrong + // package but this is fine since first, this is a cheating app and + // second there is no way to get the correct package anyway. + return packageNames[0]; + } + + String[] computeValidReportedPackages(int callingUid, + String targetPackage, int targetUid) { + if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) { + // Empty array means any package is Okay + return EmptyArray.STRING; + } + // IMPORTANT: The target package is already vetted to be in the target UID + String[] uidPackages = new String[]{targetPackage}; + // Appwidget hosts get to pass packages for widgets they host + final AppWidgetManagerInternal appWidgetManager = getAppWidgetManager(); + if (appWidgetManager != null) { + final ArraySet widgetPackages = appWidgetManager + .getHostedWidgetPackages(targetUid); + if (widgetPackages != null && !widgetPackages.isEmpty()) { + final String[] validPackages = new String[uidPackages.length + + widgetPackages.size()]; + System.arraycopy(uidPackages, 0, validPackages, 0, uidPackages.length); + final int widgetPackageCount = widgetPackages.size(); + for (int i = 0; i < widgetPackageCount; i++) { + validPackages[uidPackages.length + i] = widgetPackages.valueAt(i); + } + return validPackages; + } + } + return uidPackages; + } + public void clearWindowsLocked() { List windows = Collections.emptyList(); final int activeWindowId = mActiveWindowId; @@ -4932,7 +5062,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public final RemoteCallbackList mUserClients = new RemoteCallbackList<>(); - public final SparseArray mInteractionConnections = + public final SparseArray mInteractionConnections = new SparseArray<>(); public final SparseArray mWindowTokens = new SparseArray<>(); diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 80b54770e4b7..a57010b27037 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -29,6 +29,7 @@ import android.app.PendingIntent; import android.app.admin.DevicePolicyManagerInternal; import android.app.admin.DevicePolicyManagerInternal.OnCrossProfileWidgetProvidersChangeListener; import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetManagerInternal; import android.appwidget.AppWidgetProviderInfo; import android.appwidget.PendingHostUpdate; import android.content.BroadcastReceiver; @@ -99,6 +100,7 @@ import com.android.internal.appwidget.IAppWidgetService; import com.android.internal.os.BackgroundThread; import com.android.internal.os.SomeArgs; import com.android.internal.util.DumpUtils; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.widget.IRemoteViewsAdapterConnection; import com.android.internal.widget.IRemoteViewsFactory; @@ -107,6 +109,7 @@ import com.android.server.WidgetBackupProvider; import com.android.server.policy.IconUtilities; import libcore.io.IoUtils; +import libcore.util.EmptyArray; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -256,6 +259,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku computeMaximumWidgetBitmapMemory(); registerBroadcastReceiver(); registerOnCrossProfileProvidersChangedListener(); + + LocalServices.addService(AppWidgetManagerInternal.class, new AppWidgetManagerLocal()); } private void computeMaximumWidgetBitmapMemory() { @@ -4709,4 +4714,24 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } } } + + private class AppWidgetManagerLocal extends AppWidgetManagerInternal { + @Override + public ArraySet getHostedWidgetPackages(int uid) { + synchronized (mLock) { + ArraySet widgetPackages = null; + final int widgetCount = mWidgets.size(); + for (int i = 0; i < widgetCount; i++) { + final Widget widget = mWidgets.get(i); + if (widget.host.id.uid == uid) { + if (widgetPackages == null) { + widgetPackages = new ArraySet<>(); + } + widgetPackages.add(widget.provider.id.componentName.getPackageName()); + } + } + return widgetPackages; + } + } + } } -- cgit v1.2.3 From 4ab6bce2d969cceeafe29b59a4e77d41e292ae4e Mon Sep 17 00:00:00 2001 From: y Date: Thu, 5 Apr 2018 17:57:27 -0700 Subject: ResStringPool: Fix security vulnerability Adds detection of attacker-modified size and data fields passed to ResStringPool::setTo(). These attacks are modified apks that AAPT would not normally generate. In the rare case this occurs, the installation cannot be allowed to continue. Bug: 71361168 Bug: 71360999 Test: run cts -m CtsAppSecurityHostTestCases \ -t android.appsecurity.cts.CorruptApkTests Change-Id: If7eb93a9e723b16c8a0556fc4e20006aa0391d57 Merged-In: If7eb93a9e723b16c8a0556fc4e20006aa0391d57 (cherry picked from commit 7e54c3f261d81316b75cb734075319108d8bc1d1) --- libs/androidfw/ResourceTypes.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 733ffb180b3b..401c7b070188 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -457,6 +457,22 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) uninit(); + // The chunk must be at least the size of the string pool header. + if (size < sizeof(ResStringPool_header)) { + LOG_ALWAYS_FATAL("Bad string block: data size %zu is too small to be a string block", size); + return (mError=BAD_TYPE); + } + + // The data is at least as big as a ResChunk_header, so we can safely validate the other + // header fields. + // `data + size` is safe because the source of `size` comes from the kernel/filesystem. + if (validate_chunk(reinterpret_cast(data), sizeof(ResStringPool_header), + reinterpret_cast(data) + size, + "ResStringPool_header") != NO_ERROR) { + LOG_ALWAYS_FATAL("Bad string block: malformed block dimensions"); + return (mError=BAD_TYPE); + } + const bool notDeviceEndian = htods(0xf0) != 0xf0; if (copyData || notDeviceEndian) { @@ -468,6 +484,8 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) data = mOwnedData; } + // The size has been checked, so it is safe to read the data in the ResStringPool_header + // data structure. mHeader = (const ResStringPool_header*)data; if (notDeviceEndian) { -- cgit v1.2.3 From fcd576788c6f1457a35fc74c88f91c528e2c8d6e Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Thu, 12 Apr 2018 14:45:14 -0700 Subject: DO NOT MERGE (O) Revoke permision when group changed If a run time permission of a group is already granted we grant the other permission of the group automatically when requested. Hence if an already granted permission changed its group during an update suddenly permission of a potentially not approved group will get auto-granted. This is undesirable, hence we revoke the permission during the update process. Test: atest android.permission.cts.PermissionGroupChange Change-Id: Ib2165d1ae53b80455ebe02e07775853e37a2e339 Fixes: 72710897 (cherry picked from commit 0ed1b472af194896fcf421dc862c443343c7c127) --- .../android/server/pm/PackageManagerService.java | 105 +++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 06600bf75ffa..d6b572835950 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -184,6 +184,7 @@ import android.database.ContentObserver; import android.graphics.Bitmap; import android.hardware.display.DisplayManager; import android.net.Uri; +import android.os.AsyncTask; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -5849,6 +5850,83 @@ public class PackageManagerService extends IPackageManager.Stub killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED); } + /** + * We might auto-grant permissions if any permission of the group is already granted. Hence if + * the group of a granted permission changes we need to revoke it to avoid having permissions of + * the new group auto-granted. + * + * @param newPackage The new package that was installed + * @param oldPackage The old package that was updated + * @param allPackageNames All package names + */ + private void revokeRuntimePermissionsIfGroupChanged( + PackageParser.Package newPackage, + PackageParser.Package oldPackage, + ArrayList allPackageNames) { + final int numOldPackagePermissions = oldPackage.permissions.size(); + final ArrayMap oldPermissionNameToGroupName + = new ArrayMap<>(numOldPackagePermissions); + + for (int i = 0; i < numOldPackagePermissions; i++) { + final PackageParser.Permission permission = oldPackage.permissions.get(i); + + if (permission.group != null) { + oldPermissionNameToGroupName.put(permission.info.name, + permission.group.info.name); + } + } + + final int numNewPackagePermissions = newPackage.permissions.size(); + for (int newPermissionNum = 0; newPermissionNum < numNewPackagePermissions; + newPermissionNum++) { + final PackageParser.Permission newPermission = + newPackage.permissions.get(newPermissionNum); + final int newProtection = newPermission.info.protectionLevel; + + if ((newProtection & PermissionInfo.PROTECTION_DANGEROUS) != 0) { + final String permissionName = newPermission.info.name; + final String newPermissionGroupName = + newPermission.group == null ? null : newPermission.group.info.name; + final String oldPermissionGroupName = oldPermissionNameToGroupName.get( + permissionName); + + if (newPermissionGroupName != null + && !newPermissionGroupName.equals(oldPermissionGroupName)) { + final List users = mContext.getSystemService(UserManager.class) + .getUsers(); + + final int numUsers = users.size(); + for (int userNum = 0; userNum < numUsers; userNum++) { + final int userId = users.get(userNum).id; + final int numPackages = allPackageNames.size(); + + for (int packageNum = 0; packageNum < numPackages; packageNum++) { + final String packageName = allPackageNames.get(packageNum); + + if (checkPermission(permissionName, packageName, userId) + == PackageManager.PERMISSION_GRANTED) { + EventLog.writeEvent(0x534e4554, "72710897", + newPackage.applicationInfo.uid, + "Revoking permission", permissionName, "from package", + packageName, "as the group changed from", + oldPermissionGroupName, "to", newPermissionGroupName); + + try { + revokeRuntimePermission(packageName, permissionName, userId, + false); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Could not revoke " + permissionName + " from " + + packageName, e); + } + } + } + } + } + } + } + } + + /** * Get the first event id for the permission. * @@ -10743,6 +10821,8 @@ public class PackageManagerService extends IPackageManager.Stub String primaryCpuAbiFromSettings = null; String secondaryCpuAbiFromSettings = null; + final PackageParser.Package oldPkg; + // writer synchronized (mPackages) { if (pkg.mSharedUserId != null) { @@ -10843,6 +10923,12 @@ public class PackageManagerService extends IPackageManager.Stub final PackageSetting disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(pkg.packageName); + if (oldPkgSetting == null) { + oldPkg = null; + } else { + oldPkg = oldPkgSetting.pkg; + } + String[] usesStaticLibraries = null; if (pkg.usesStaticLibraries != null) { usesStaticLibraries = new String[pkg.usesStaticLibraries.size()]; @@ -11175,6 +11261,25 @@ public class PackageManagerService extends IPackageManager.Stub mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId); } } + + if (oldPkg != null) { + // We need to call revokeRuntimePermissionsIfGroupChanged async as permission + // revokation from this method might need to kill apps which need the + // mPackages lock on a different thread. This would dead lock. + // + // Hence create a copy of all package names and pass it into + // revokeRuntimePermissionsIfGroupChanged. Only for those permissions might get + // revoked. If a new package is added before the async code runs the permission + // won't be granted yet, hence new packages are no problem. + final ArrayList allPackageNames = new ArrayList<>(mPackages.keySet()); + + AsyncTask.execute(new Runnable() { + public void run() { + revokeRuntimePermissionsIfGroupChanged(pkg, oldPkg, allPackageNames); + } + }); + } + return pkg; } -- cgit v1.2.3 From 2c8e19daa390addcacaeefa59df110a13ca911c6 Mon Sep 17 00:00:00 2001 From: sqian Date: Fri, 13 Apr 2018 17:10:05 -0700 Subject: Fix broken check for TelephonyManager#getForbiddenPlmns (backport from a fix merged in pi-dev) Bug: 73884967 Test: Treehugger Change-Id: I9deaae20893184cde36dcd936fe83708fa60b830 Merged-In: I0cf7920e138892fbcab71fae0eed1293f0b2e404 Merged-In: I9e3456e5f1e479b0e2b102f6c90db57cd0e977fe (cherry picked from commit 7b52a48d6b10e3ed2806b57d39a2d9211dd4b585) --- telephony/java/android/telephony/TelephonyManager.java | 2 +- telephony/java/com/android/internal/telephony/ITelephony.aidl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index c9afb9f9ea31..d4a806621362 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -4605,7 +4605,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony == null) return null; - return telephony.getForbiddenPlmns(subId, appType); + return telephony.getForbiddenPlmns(subId, appType, mContext.getOpPackageName()); } catch (RemoteException ex) { return null; } catch (NullPointerException ex) { diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 9262ec5ed53b..235b5eecb986 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -1352,12 +1352,12 @@ interface ITelephony { * Returns null if the query fails. * * - *

Requires that the calling app has READ_PRIVILEGED_PHONE_STATE + *

Requires that the calling app has READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE * * @param subId subscription ID used for authentication * @param appType the icc application type, like {@link #APPTYPE_USIM} */ - String[] getForbiddenPlmns(int subId, int appType); + String[] getForbiddenPlmns(int subId, int appType, String callingPackage); /** * Check if phone is in emergency callback mode -- cgit v1.2.3 From 9214edb61c5af7f33f7c0c861966082d9a28e21f Mon Sep 17 00:00:00 2001 From: Hansong Zhang Date: Thu, 26 Apr 2018 14:22:39 -0700 Subject: DO NOT MERGE Truncate newline and tab characters in BluetoothDevice name Test: manual Bug: 73173182 Change-Id: I7f2201cab36adf7f01d1a794d783cb78a536811f (cherry picked from commit 24da173b63b17a0bc6c80b2fcfefa7fe4574a15b) --- core/java/android/bluetooth/BluetoothDevice.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 98cd319a399a..9498f125c2a6 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -808,7 +808,11 @@ public final class BluetoothDevice implements Parcelable { return null; } try { - return service.getRemoteName(this); + String name = service.getRemoteName(this); + if (name != null) { + return name.replaceAll("[\\t\\n\\r]+", " "); + } + return null; } catch (RemoteException e) { Log.e(TAG, "", e); } -- cgit v1.2.3 From 1112a8b64849ce8b366d7cc933b7080ad66be514 Mon Sep 17 00:00:00 2001 From: Ecco Park Date: Thu, 3 May 2018 14:49:40 -0700 Subject: Osu: fixed Mismatch between createFromParcel and writeToParcel Bug: 77600924 Change-Id: I46d765892e8e6839ed5140a3b0d6bb1815ccf9bc Signed-off-by: Ecco Park (cherry picked from commit 9a59cf84506e9fa841524ac2c70ae683449e709a) --- packages/Osu/src/com/android/hotspot2/flow/OSUInfo.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/Osu/src/com/android/hotspot2/flow/OSUInfo.java b/packages/Osu/src/com/android/hotspot2/flow/OSUInfo.java index 401eccb96d05..ae47ddd22fc3 100644 --- a/packages/Osu/src/com/android/hotspot2/flow/OSUInfo.java +++ b/packages/Osu/src/com/android/hotspot2/flow/OSUInfo.java @@ -42,6 +42,7 @@ public class OSUInfo implements Parcelable { private HSIconFileElement mIconFileElement; private String mIconFileName; private IconInfo mIconInfo; + private int mIconIndex; public OSUInfo(ScanResult scanResult, OSUProvider osuProvider, int osuID) { mOsuID = osuID; @@ -50,6 +51,7 @@ public class OSUInfo implements Parcelable { mAnqpDomID = scanResult.anqpDomainId; mAdvertisingSSID = scanResult.SSID; mOSUProvider = osuProvider; + mIconIndex = -1; } public long getOSUBssid() { @@ -157,12 +159,15 @@ public class OSUInfo implements Parcelable { public void setIconFileElement(HSIconFileElement iconFileElement, String fileName) { synchronized (mOSUProvider) { mIconFileElement = iconFileElement; + int index = 0; for (IconInfo iconInfo : mOSUProvider.getIcons()) { if (iconInfo.getFileName().equals(fileName)) { mIconInfo = iconInfo; mIconFileName = fileName; + mIconIndex = index; break; } + index++; } mIconStatus = IconStatus.Available; } @@ -285,9 +290,9 @@ public class OSUInfo implements Parcelable { return; } mIconFileElement = new HSIconFileElement(in); - int iconIndex = in.readInt(); - mIconInfo = iconIndex >= 0 && iconIndex < mOSUProvider.getIcons().size() - ? mOSUProvider.getIcons().get(iconIndex) : null; + mIconIndex = in.readInt(); + mIconInfo = mIconIndex >= 0 && mIconIndex < mOSUProvider.getIcons().size() + ? mOSUProvider.getIcons().get(mIconIndex) : null; } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @@ -317,5 +322,6 @@ public class OSUInfo implements Parcelable { dest.writeInt(mIconStatus.ordinal()); mOSUProvider.writeParcel(dest); mIconFileElement.writeParcel(dest); + dest.writeInt(mIconIndex); } } -- cgit v1.2.3 From 275d49fd349461ea58d320bb66cb572e67a94e02 Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Mon, 11 Dec 2017 10:07:03 -0800 Subject: Nullcheck to fix Autofill CTS Test: presubmit Fixes: 70506475 Bug: 69981755 Change-Id: I187bed4889a4901a7137a2995178ea651ed09186 (cherry picked from commit 6c68a692880b7cc981e130aace0edb9f2fcffff6) --- .../view/accessibility/AccessibilityInteractionClient.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index be3b34d0ccf8..980a2c926c1a 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -734,11 +734,14 @@ public final class AccessibilityInteractionClient if (info != null) { info.setConnectionId(connectionId); // Empty array means any package name is Okay - if (!ArrayUtils.isEmpty(packageNames) - && !ArrayUtils.contains(packageNames, info.getPackageName().toString())) { - // If the node package not one of the valid ones, pick the top one - this - // is one of the packages running in the introspected UID. - info.setPackageName(packageNames[0]); + if (!ArrayUtils.isEmpty(packageNames)) { + CharSequence packageName = info.getPackageName(); + if (packageName == null + || !ArrayUtils.contains(packageNames, packageName.toString())) { + // If the node package not one of the valid ones, pick the top one - this + // is one of the packages running in the introspected UID. + info.setPackageName(packageNames[0]); + } } info.setSealed(true); if (!bypassCache) { -- cgit v1.2.3 From 13343c13591f6a3054eee33c2748bf656d7e90f3 Mon Sep 17 00:00:00 2001 From: Tony Mak Date: Thu, 14 Dec 2017 12:40:07 +0000 Subject: clearCallingIdentity before calling into getPackageUidAsUser Fix: 70585244 Bug: 69981755 Test: Enable any accessibility service -> inflate work profile -> Tap on any work app -> no longer crash Test: cts-tradefed run cts-dev --module DevicePolicyManager --test com.android.cts.devicepolicy.CrossProfileAppsHostSideTest.testPrimaryUserToManagedProfile Change-Id: I80d18f4e2ab76a228cb0aa2c8312c323a9b5c84d (cherry picked from commit 857326e3731939f6ec7979e1d86585bf0ea484f4) --- .../com/android/server/accessibility/AccessibilityManagerService.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index d0d65d94fb5b..8b4076af7759 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -4566,11 +4566,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } private boolean isValidPackageForUid(String packageName, int uid) { + final long token = Binder.clearCallingIdentity(); try { return uid == mPackageManager.getPackageUid( packageName, UserHandle.getUserId(uid)); } catch (PackageManager.NameNotFoundException e) { return false; + } finally { + Binder.restoreCallingIdentity(token); } } -- cgit v1.2.3 From 007288b7a9177b63e312d8a6e3c6f33007066ad7 Mon Sep 17 00:00:00 2001 From: Benedict Wong Date: Thu, 3 May 2018 21:07:58 -0700 Subject: DO NOT MERGE: Fix ConnectivityController meteredness checks This patch corrects ConnectivityController's meteredness checks to perform correct meteredness checks while VPNs are running. This fixes a bug in O-MR1 where any apps using the DownloadProvider with unmetered network constraints fail to start while the VPN is enabled. This change adds a bespoke method for ConnectivityController, allowing it to correctly identify the meteredness without affecting public API surfaces. Bug: 78644887 Test: Built, flashed on Walleye, and tested. Test: Additional test coverage in subsequent patch(es). Change-Id: Ie1d11d93d51d936ce81cd5984af61bde30325983 (cherry picked from commit d08ab5a641d9d81314c9439724dd34338fa81d58) --- core/java/android/net/ConnectivityManager.java | 22 ++++++++++++++++++++++ core/java/android/net/IConnectivityManager.aidl | 1 + .../com/android/server/ConnectivityService.java | 12 +++++++++++- .../job/controllers/ConnectivityController.java | 6 ++---- 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index d7ecc81ffdba..467eb9b0b0bf 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -2504,6 +2504,28 @@ public class ConnectivityManager { } } + /** + * Returns if the active data network for the given UID is metered. A network + * is classified as metered when the user is sensitive to heavy data usage on + * that connection due to monetary costs, data limitations or + * battery/performance issues. You should check this before doing large + * data transfers, and warn the user or delay the operation until another + * network is available. + * + * @return {@code true} if large transfers should be avoided, otherwise + * {@code false}. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) + public boolean isActiveNetworkMeteredForUid(int uid) { + try { + return mService.isActiveNetworkMeteredForUid(uid); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * If the LockdownVpn mechanism is enabled, updates the vpn * with a reload of its profile. diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index a6fe7389bc72..f11372c2b31c 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -66,6 +66,7 @@ interface IConnectivityManager NetworkQuotaInfo getActiveNetworkQuotaInfo(); boolean isActiveNetworkMetered(); + boolean isActiveNetworkMeteredForUid(int uid); boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress); diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index c1801b80af0d..c2b83d98a8df 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -1339,7 +1339,17 @@ public class ConnectivityService extends IConnectivityManager.Stub public boolean isActiveNetworkMetered() { enforceAccessPermission(); - final int uid = Binder.getCallingUid(); + return isActiveNetworkMeteredCommon(Binder.getCallingUid()); + } + + @Override + public boolean isActiveNetworkMeteredForUid(int uid) { + enforceConnectivityInternalPermission(); + + return isActiveNetworkMeteredCommon(uid); + } + + private boolean isActiveNetworkMeteredCommon(int uid) { final NetworkCapabilities caps = getUnfilteredActiveNetworkState(uid).networkCapabilities; if (caps != null) { return !caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java index 78367fe97a54..4d5a920dd54f 100644 --- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java +++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java @@ -112,10 +112,8 @@ public final class ConnectivityController extends StateController implements final boolean connected = (info != null) && info.isConnected(); final boolean connectionUsable = connected && validated; - final boolean metered = connected && (capabilities != null) - && !capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); - final boolean unmetered = connected && (capabilities != null) - && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); + final boolean metered = connected && mConnManager.isActiveNetworkMeteredForUid(jobUid); + final boolean unmetered = connected && !mConnManager.isActiveNetworkMeteredForUid(jobUid); final boolean notRoaming = connected && (info != null) && !info.isRoaming(); -- cgit v1.2.3 From eef265cf5e5097e66adaf502260404fce32fca37 Mon Sep 17 00:00:00 2001 From: Benedict Wong Date: Tue, 20 Feb 2018 15:19:59 -0800 Subject: DO NOT MERGE: Add unit tests to ensure VPN meteredness These new tests ensure that VPNs report the meteredness of their underlying networks correctly. The added test verifies VPN meteredness for cases of metered and unmetered WiFi and Cell Bug: 78644887 Test: This; ran on walleye-eng Change-Id: I28bdc71a336bfd97f7908455d4781d774df44b87 (cherry picked from commit 66bc52884b1009fca7917ae89e72e8aa40f394d1) --- .../com/android/server/ConnectivityService.java | 58 +++++++++------ .../android/server/ConnectivityServiceTest.java | 87 ++++++++++++++++++++++ 2 files changed, 124 insertions(+), 21 deletions(-) diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index c2b83d98a8df..6e8c0d4a55c1 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -969,7 +969,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (!mLockdownEnabled) { int user = UserHandle.getUserId(uid); synchronized (mVpns) { - Vpn vpn = mVpns.get(user); + Vpn vpn = getVpn(user); if (vpn != null && vpn.appliesToUid(uid)) { return vpn.getUnderlyingNetworks(); } @@ -1017,7 +1017,7 @@ public class ConnectivityService extends IConnectivityManager.Stub return false; } synchronized (mVpns) { - final Vpn vpn = mVpns.get(UserHandle.getUserId(uid)); + final Vpn vpn = getVpn(UserHandle.getUserId(uid)); if (vpn != null && vpn.isBlockingUid(uid)) { return true; } @@ -1094,7 +1094,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final int user = UserHandle.getUserId(uid); int vpnNetId = NETID_UNSET; synchronized (mVpns) { - final Vpn vpn = mVpns.get(user); + final Vpn vpn = getVpn(user); if (vpn != null && vpn.appliesToUid(uid)) vpnNetId = vpn.getNetId(); } NetworkAgentInfo nai; @@ -1224,7 +1224,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (!mLockdownEnabled) { synchronized (mVpns) { - Vpn vpn = mVpns.get(userId); + Vpn vpn = getVpn(userId); if (vpn != null) { Network[] networks = vpn.getUnderlyingNetworks(); if (networks != null) { @@ -3424,7 +3424,7 @@ public class ConnectivityService extends IConnectivityManager.Stub throwIfLockdownEnabled(); synchronized (mVpns) { - Vpn vpn = mVpns.get(userId); + Vpn vpn = getVpn(userId); if (vpn != null) { return vpn.prepare(oldPackage, newPackage); } else { @@ -3451,7 +3451,7 @@ public class ConnectivityService extends IConnectivityManager.Stub enforceCrossUserPermission(userId); synchronized (mVpns) { - Vpn vpn = mVpns.get(userId); + Vpn vpn = getVpn(userId); if (vpn != null) { vpn.setPackageAuthorization(packageName, authorized); } @@ -3470,7 +3470,7 @@ public class ConnectivityService extends IConnectivityManager.Stub throwIfLockdownEnabled(); int user = UserHandle.getUserId(Binder.getCallingUid()); synchronized (mVpns) { - return mVpns.get(user).establish(config); + return getVpn(user).establish(config); } } @@ -3487,7 +3487,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } int user = UserHandle.getUserId(Binder.getCallingUid()); synchronized (mVpns) { - mVpns.get(user).startLegacyVpn(profile, mKeyStore, egress); + getVpn(user).startLegacyVpn(profile, mKeyStore, egress); } } @@ -3501,7 +3501,7 @@ public class ConnectivityService extends IConnectivityManager.Stub enforceCrossUserPermission(userId); synchronized (mVpns) { - return mVpns.get(userId).getLegacyVpnInfo(); + return getVpn(userId).getLegacyVpnInfo(); } } @@ -3565,7 +3565,7 @@ public class ConnectivityService extends IConnectivityManager.Stub public VpnConfig getVpnConfig(int userId) { enforceCrossUserPermission(userId); synchronized (mVpns) { - Vpn vpn = mVpns.get(userId); + Vpn vpn = getVpn(userId); if (vpn != null) { return vpn.getVpnConfig(); } else { @@ -3599,7 +3599,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } int user = UserHandle.getUserId(Binder.getCallingUid()); synchronized (mVpns) { - Vpn vpn = mVpns.get(user); + Vpn vpn = getVpn(user); if (vpn == null) { Slog.w(TAG, "VPN for user " + user + " not ready yet. Skipping lockdown"); return false; @@ -3646,7 +3646,7 @@ public class ConnectivityService extends IConnectivityManager.Stub */ private boolean startAlwaysOnVpn(int userId) { synchronized (mVpns) { - Vpn vpn = mVpns.get(userId); + Vpn vpn = getVpn(userId); if (vpn == null) { // Shouldn't happen as all codepaths that point here should have checked the Vpn // exists already. @@ -3664,7 +3664,7 @@ public class ConnectivityService extends IConnectivityManager.Stub enforceCrossUserPermission(userId); synchronized (mVpns) { - Vpn vpn = mVpns.get(userId); + Vpn vpn = getVpn(userId); if (vpn == null) { Slog.w(TAG, "User " + userId + " has no Vpn configuration"); return false; @@ -3684,7 +3684,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } synchronized (mVpns) { - Vpn vpn = mVpns.get(userId); + Vpn vpn = getVpn(userId); if (vpn == null) { Slog.w(TAG, "User " + userId + " has no Vpn configuration"); return false; @@ -3706,7 +3706,7 @@ public class ConnectivityService extends IConnectivityManager.Stub enforceCrossUserPermission(userId); synchronized (mVpns) { - Vpn vpn = mVpns.get(userId); + Vpn vpn = getVpn(userId); if (vpn == null) { Slog.w(TAG, "User " + userId + " has no Vpn configuration"); return null; @@ -3852,22 +3852,38 @@ public class ConnectivityService extends IConnectivityManager.Stub private void onUserStart(int userId) { synchronized (mVpns) { - Vpn userVpn = mVpns.get(userId); + Vpn userVpn = getVpn(userId); if (userVpn != null) { loge("Starting user already has a VPN"); return; } userVpn = new Vpn(mHandler.getLooper(), mContext, mNetd, userId); - mVpns.put(userId, userVpn); + setVpn(userId, userVpn); } if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) { updateLockdownVpn(); } } + /** @hide */ + @VisibleForTesting + Vpn getVpn(int userId) { + synchronized (mVpns) { + return mVpns.get(userId); + } + } + + /** @hide */ + @VisibleForTesting + void setVpn(int userId, Vpn userVpn) { + synchronized (mVpns) { + mVpns.put(userId, userVpn); + } + } + private void onUserStop(int userId) { synchronized (mVpns) { - Vpn userVpn = mVpns.get(userId); + Vpn userVpn = getVpn(userId); if (userVpn == null) { loge("Stopped user has no VPN"); return; @@ -5439,7 +5455,7 @@ public class ConnectivityService extends IConnectivityManager.Stub throwIfLockdownEnabled(); int user = UserHandle.getUserId(Binder.getCallingUid()); synchronized (mVpns) { - return mVpns.get(user).addAddress(address, prefixLength); + return getVpn(user).addAddress(address, prefixLength); } } @@ -5448,7 +5464,7 @@ public class ConnectivityService extends IConnectivityManager.Stub throwIfLockdownEnabled(); int user = UserHandle.getUserId(Binder.getCallingUid()); synchronized (mVpns) { - return mVpns.get(user).removeAddress(address, prefixLength); + return getVpn(user).removeAddress(address, prefixLength); } } @@ -5458,7 +5474,7 @@ public class ConnectivityService extends IConnectivityManager.Stub int user = UserHandle.getUserId(Binder.getCallingUid()); boolean success; synchronized (mVpns) { - success = mVpns.get(user).setUnderlyingNetworks(networks); + success = getVpn(user).setUnderlyingNetworks(networks); } if (success) { notifyIfacesChangedForNetworkStats(); diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 6674f20317b3..504a2dbc362b 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -22,6 +22,7 @@ import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA; import static android.net.ConnectivityManager.TYPE_MOBILE_MMS; import static android.net.ConnectivityManager.TYPE_NONE; +import static android.net.ConnectivityManager.TYPE_VPN; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.getNetworkTypeName; import static android.net.NetworkCapabilities.*; @@ -102,6 +103,7 @@ import com.android.server.connectivity.MockableSystemProperties; import com.android.server.connectivity.NetworkAgentInfo; import com.android.server.connectivity.NetworkMonitor; import com.android.server.connectivity.NetworkMonitor.CaptivePortalProbeResult; +import com.android.server.connectivity.Vpn; import com.android.server.net.NetworkPinner; import com.android.server.net.NetworkPolicyManagerInternal; @@ -333,6 +335,9 @@ public class ConnectivityServiceTest extends AndroidTestCase { case TRANSPORT_WIFI_AWARE: mScore = 20; break; + case TRANSPORT_VPN: + mScore = 0; + break; default: throw new UnsupportedOperationException("unimplemented network type"); } @@ -868,6 +873,8 @@ public class ConnectivityServiceTest extends AndroidTestCase { return TYPE_WIFI; case TRANSPORT_CELLULAR: return TYPE_MOBILE; + case TRANSPORT_VPN: + return TYPE_VPN; default: return TYPE_NONE; } @@ -3447,4 +3454,84 @@ public class ConnectivityServiceTest extends AndroidTestCase { return; } } + + @SmallTest + public void testVpnNetworkMetered() { + final TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(callback); + + final NetworkRequest cellRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR).build(); + final TestNetworkCallback cellCallback = new TestNetworkCallback(); + mCm.registerNetworkCallback(cellRequest, cellCallback); + + // Setup cellular + mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent); + cellCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent); + verifyActiveNetwork(TRANSPORT_CELLULAR); + + // Verify meteredness of cellular + assertTrue(mCm.isActiveNetworkMetered()); + + // Setup Wifi + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + callback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent); + cellCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent); + verifyActiveNetwork(TRANSPORT_WIFI); + + // Verify meteredness of WiFi + assertTrue(mCm.isActiveNetworkMetered()); + + // Verify that setting unmetered on Wifi changes ActiveNetworkMetered + mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + callback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mWiFiNetworkAgent); + assertFalse(mCm.isActiveNetworkMetered()); + + // Setup VPN + final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); + vpnNetworkAgent.connect(true); + + Vpn mockVpn = mock(Vpn.class); + when(mockVpn.appliesToUid(anyInt())).thenReturn(true); + when(mockVpn.getNetId()).thenReturn(vpnNetworkAgent.getNetwork().netId); + + Vpn oldVpn = mService.getVpn(UserHandle.myUserId()); + mService.setVpn(UserHandle.myUserId(), mockVpn); + assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + + // Verify meteredness of VPN on default network + when(mockVpn.getUnderlyingNetworks()).thenReturn(null); + assertFalse(mCm.isActiveNetworkMetered()); + assertFalse(mCm.isActiveNetworkMeteredForUid(Process.myUid())); + + // Verify meteredness of VPN on unmetered wifi + when(mockVpn.getUnderlyingNetworks()) + .thenReturn(new Network[] {mWiFiNetworkAgent.getNetwork()}); + assertFalse(mCm.isActiveNetworkMetered()); + assertFalse(mCm.isActiveNetworkMeteredForUid(Process.myUid())); + + // Set WiFi as metered, then check to see that it has been updated on the VPN + mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED); + callback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED, mWiFiNetworkAgent); + assertTrue(mCm.isActiveNetworkMetered()); + assertTrue(mCm.isActiveNetworkMeteredForUid(Process.myUid())); + + // Switch to cellular + when(mockVpn.getUnderlyingNetworks()) + .thenReturn(new Network[] {mCellNetworkAgent.getNetwork()}); + assertTrue(mCm.isActiveNetworkMetered()); + assertTrue(mCm.isActiveNetworkMeteredForUid(Process.myUid())); + + // Test unmetered cellular + mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + cellCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent); + assertFalse(mCm.isActiveNetworkMetered()); + assertFalse(mCm.isActiveNetworkMeteredForUid(Process.myUid())); + + mService.setVpn(UserHandle.myUserId(), oldVpn); + mCm.unregisterNetworkCallback(callback); + } } -- cgit v1.2.3 From 03af9f281932c163617f5dd01f1371e026d9de49 Mon Sep 17 00:00:00 2001 From: Adrian Roos Date: Tue, 22 May 2018 16:56:35 +0200 Subject: WM: Prevent secondary display focus while keyguard is up Fixes an issue where input intended for the keyguard could end up going to a different display. To prevent this, make sure that only the default display can get focused when the keyguard is showing. Change-Id: I6463c44aedca06930d2c9bda7c45ffd93141308c Merged-In: I6463c44aedca06930d2c9bda7c45ffd93141308c Fixes: 71786287 Test: atest DisplayContentTests (cherry picked from commit 3cd5e3d9bbb3255e874b8fa27d7ed506164905dd) --- core/java/android/view/WindowManagerPolicy.java | 5 +++++ .../com/android/server/policy/PhoneWindowManager.java | 5 +++++ .../server/policy/keyguard/KeyguardStateMonitor.java | 3 +++ .../com/android/server/wm/RootWindowContainer.java | 8 ++++++++ .../com/android/server/wm/WindowManagerService.java | 13 +++++++++++++ .../com/android/server/wm/DisplayContentTests.java | 19 +++++++++++++++++++ .../android/server/wm/TestWindowManagerPolicy.java | 5 +++-- 7 files changed, 56 insertions(+), 2 deletions(-) diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index c4ffb4c06a26..235e61a129f6 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -610,6 +610,11 @@ public interface WindowManagerPolicy { */ void notifyKeyguardTrustedChanged(); + /** + * The keyguard showing state has changed + */ + void onKeyguardShowingAndNotOccludedChanged(); + /** * Notifies the window manager that screen is being turned off. * diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index da14c360f16a..d3469c2346a5 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -2150,6 +2150,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { public void onTrustedChanged() { mWindowManagerFuncs.notifyKeyguardTrustedChanged(); } + + @Override + public void onShowingChanged() { + mWindowManagerFuncs.onKeyguardShowingAndNotOccludedChanged(); + } }); } diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java index 941cd4441e23..fd34c510d98d 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java @@ -86,6 +86,8 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub { @Override // Binder interface public void onShowingStateChanged(boolean showing) { mIsShowing = showing; + + mCallback.onShowingChanged(); } @Override // Binder interface @@ -119,6 +121,7 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub { public interface StateCallback { void onTrustedChanged(); + void onShowingChanged(); } public void dump(String prefix, PrintWriter pw) { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index a9e56f34b0d6..5a75262a8023 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -165,10 +165,18 @@ class RootWindowContainer extends WindowContainer { } WindowState computeFocusedWindow() { + // While the keyguard is showing, we must focus anything besides the main display. + // Otherwise we risk input not going to the keyguard when the user expects it to. + final boolean forceDefaultDisplay = mService.mPolicy.isKeyguardShowingAndNotOccluded(); + for (int i = mChildren.size() - 1; i >= 0; i--) { final DisplayContent dc = mChildren.get(i); final WindowState win = dc.findFocusedWindow(); if (win != null) { + if (forceDefaultDisplay && !dc.isDefaultDisplay) { + EventLog.writeEvent(0x534e4554, "71786287", win.mOwnerUid, ""); + continue; + } return win; } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index f5cc43bd9019..d378fa3ca229 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2928,6 +2928,11 @@ public class WindowManagerService extends IWindowManager.Stub mH.sendEmptyMessage(H.NOTIFY_KEYGUARD_TRUSTED_CHANGED); } + @Override + public void onKeyguardShowingAndNotOccludedChanged() { + mH.sendEmptyMessage(H.RECOMPUTE_FOCUS); + } + @Override public void screenTurningOff(ScreenOffListener listener) { mTaskSnapshotController.screenTurningOff(listener); @@ -4897,6 +4902,7 @@ public class WindowManagerService extends IWindowManager.Stub public static final int NOTIFY_KEYGUARD_FLAGS_CHANGED = 56; public static final int NOTIFY_KEYGUARD_TRUSTED_CHANGED = 57; public static final int SET_HAS_OVERLAY_UI = 58; + public static final int RECOMPUTE_FOCUS = 61; /** * Used to denote that an integer field in a message will not be used. @@ -5363,6 +5369,13 @@ public class WindowManagerService extends IWindowManager.Stub mAmInternal.setHasOverlayUi(msg.arg1, msg.arg2 == 1); } break; + case RECOMPUTE_FOCUS: { + synchronized (mWindowMap) { + updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, + true /* updateInputWindows */); + } + } + break; } if (DEBUG_WINDOW_TRACE) { Slog.v(TAG_WM, "handleMessage: exit"); diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java index 10d241357ff4..a6cada7b42e2 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java @@ -313,6 +313,25 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals(window1, sWm.mRoot.computeFocusedWindow()); } + @Test + public void testKeyguard_preventsSecondaryDisplayFocus() throws Exception { + final WindowState keyguard = createWindow(null, TYPE_STATUS_BAR, + sWm.getDefaultDisplayContentLocked(), "keyguard"); + assertEquals(keyguard, sWm.mRoot.computeFocusedWindow()); + + // Add a window to a second display, and it should be focused + final DisplayContent dc = createNewDisplay(); + final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win"); + assertEquals(win, sWm.mRoot.computeFocusedWindow()); + + ((TestWindowManagerPolicy)sWm.mPolicy).keyguardShowingAndNotOccluded = true; + try { + assertEquals(keyguard, sWm.mRoot.computeFocusedWindow()); + } finally { + ((TestWindowManagerPolicy)sWm.mPolicy).keyguardShowingAndNotOccluded = false; + } + } + /** * This tests setting the maximum ui width on a display. */ diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java index eca27eefb2cd..27ea325b8d80 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -63,6 +63,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { private static WindowManagerService sWm = null; int rotationToReport = 0; + boolean keyguardShowingAndNotOccluded = false; private Runnable mRunnableWhenAddingSplashScreen; @@ -420,7 +421,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { @Override public boolean isKeyguardLocked() { - return false; + return keyguardShowingAndNotOccluded; } @Override @@ -440,7 +441,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { @Override public boolean isKeyguardShowingAndNotOccluded() { - return false; + return keyguardShowingAndNotOccluded; } @Override -- cgit v1.2.3 From d0c09ae726054c9d20a245dd167ade6c5a21a9f9 Mon Sep 17 00:00:00 2001 From: Todd Kennedy Date: Thu, 3 May 2018 10:05:04 +0100 Subject: Make safe label more safe * limit the absolute maximum size of the label to 50000 characters [which is probably far more than necessary, but, can be dialed down] * use a string buffer while processing the string [instead of creating multiple string objects] Bug: 62537081 Test: Manual. Install APK in bug and see that it can be uninstalled Change-Id: Ibf63c2691ad7438a123e92110d95b1f50050f8b1 Merged-In: Ibf63c2691ad7438a123e92110d95b1f50050f8b1 (cherry picked from commit 2263da9539daef134395226a2718ba2d7af7547d) --- core/java/android/content/pm/PackageItemInfo.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java index 11830c294116..84b779466dbf 100644 --- a/core/java/android/content/pm/PackageItemInfo.java +++ b/core/java/android/content/pm/PackageItemInfo.java @@ -42,6 +42,9 @@ import java.util.Comparator; */ public class PackageItemInfo { private static final float MAX_LABEL_SIZE_PX = 500f; + /** The maximum length of a safe label, in characters */ + private static final int MAX_SAFE_LABEL_LENGTH = 50000; + /** * Public name of this item. From the "android:name" attribute. */ @@ -169,7 +172,8 @@ public class PackageItemInfo { // If the label contains new line characters it may push the UI // down to hide a part of it. Labels shouldn't have new line // characters, so just truncate at the first time one is seen. - final int labelLength = labelStr.length(); + final int labelLength = Math.min(labelStr.length(), MAX_SAFE_LABEL_LENGTH); + final StringBuffer sb = new StringBuffer(labelLength); int offset = 0; while (offset < labelLength) { final int codePoint = labelStr.codePointAt(offset); @@ -181,14 +185,19 @@ public class PackageItemInfo { break; } // replace all non-break space to " " in order to be trimmed + final int charCount = Character.charCount(codePoint); if (type == Character.SPACE_SEPARATOR) { - labelStr = labelStr.substring(0, offset) + " " + labelStr.substring(offset + - Character.charCount(codePoint)); + sb.append(' '); + } else { + sb.append(labelStr.charAt(offset)); + if (charCount == 2) { + sb.append(labelStr.charAt(offset + 1)); + } } - offset += Character.charCount(codePoint); + offset += charCount; } - labelStr = labelStr.trim(); + labelStr = sb.toString().trim(); if (labelStr.isEmpty()) { return packageName; } -- cgit v1.2.3 From 92aed32dda0a6bec5cd26f4f32d8c9b103af557b Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Mon, 21 May 2018 13:59:23 -0700 Subject: ResStringPool: Prevenet boot loop from se fix Changes the logs adding in a previous security fix to warnings so devices with malformed APKs currently on them will not undergo DOS when they are upgraded to P. Bug: 79724567 Test: run cts -m CtsAppSecurityHostTestCases \ -t android.appsecurity.cts.CorruptApkTests Change-Id: Ied54e4bb14abdaf79da562022c7ea6075187c1f8 (cherry picked from commit f05f47b2c1838529e682ad8f931d3da72244b1a1) (cherry picked from commit c31cf80008fdb06ea8e1eab9764096653e7854b1) --- libs/androidfw/ResourceTypes.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 401c7b070188..c78554f340fc 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -459,7 +459,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) // The chunk must be at least the size of the string pool header. if (size < sizeof(ResStringPool_header)) { - LOG_ALWAYS_FATAL("Bad string block: data size %zu is too small to be a string block", size); + ALOGW("Bad string block: data size %zu is too small to be a string block", size); return (mError=BAD_TYPE); } @@ -469,7 +469,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) if (validate_chunk(reinterpret_cast(data), sizeof(ResStringPool_header), reinterpret_cast(data) + size, "ResStringPool_header") != NO_ERROR) { - LOG_ALWAYS_FATAL("Bad string block: malformed block dimensions"); + ALOGW("Bad string block: malformed block dimensions"); return (mError=BAD_TYPE); } -- cgit v1.2.3 From 906afb4f3641963583a4334bd8291d479b8c844a Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Wed, 30 May 2018 12:17:01 -0700 Subject: Fix DynamicRefTable::load security bug DynamicRefTables parsed from apks are missing bounds checks that prevent buffer overflows. This changes verifies the bounds of the header before attempting to preform operations on the chunk. Bug: 79488511 Test: run cts -m CtsAppSecurityHostTestCases \ -t android.appsecurity.cts.CorruptApkTests Change-Id: I02c8ad957da244fce777ac68a482e4e8fa70f846 Merged-In: I02c8ad957da244fce777ac68a482e4e8fa70f846 (cherry picked from commit 18a6ada4aa136da4f50f03fff91d61d448ced195) --- libs/androidfw/ResourceTypes.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index c78554f340fc..4813b4c83d04 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -6576,8 +6576,16 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, } } else if (ctype == RES_TABLE_LIBRARY_TYPE) { + if (group->dynamicRefTable.entries().size() == 0) { - status_t err = group->dynamicRefTable.load((const ResTable_lib_header*) chunk); + const ResTable_lib_header* lib = (const ResTable_lib_header*) chunk; + status_t err = validate_chunk(&lib->header, sizeof(*lib), + endPos, "ResTable_lib_header"); + if (err != NO_ERROR) { + return (mError=err); + } + + err = group->dynamicRefTable.load(lib); if (err != NO_ERROR) { return (mError=err); } -- cgit v1.2.3 From faf2dc95bf26fb075039ed3ee7623e83a281c6dd Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Tue, 12 Jun 2018 13:01:42 -0700 Subject: Backport Prevent shortcut info package name spoofing Test: cts-tradefed run cts -m CtsShortcutManagerTestCases -t android.content.pm.cts.shortcutmanager.ShortcutManagerFakingPublisherTest Bug: 109824443 Change-Id: I90443973aaef157d357b98b739572866125b2bbc Merged-In: I78948446a63b428ae750464194558fd44a658493 (cherry picked from commit 9e21579a11219581a0c08ff5dd6ac4dc22e988a4) --- .../com/android/server/pm/ShortcutService.java | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 15d20716a322..83b817559c2a 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -131,6 +131,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import java.util.function.Predicate; @@ -1534,6 +1535,24 @@ public class ShortcutService extends IShortcutService.Stub { "Ephemeral apps can't use ShortcutManager"); } + private void verifyShortcutInfoPackage(String callerPackage, ShortcutInfo si) { + if (si == null) { + return; + } + if (!Objects.equals(callerPackage, si.getPackage())) { + android.util.EventLog.writeEvent(0x534e4554, "109824443", -1, ""); + throw new SecurityException("Shortcut package name mismatch"); + } + } + + private void verifyShortcutInfoPackages( + String callerPackage, List list) { + final int size = list.size(); + for (int i = 0; i < size; i++) { + verifyShortcutInfoPackage(callerPackage, list.get(i)); + } + } + // Overridden in unit tests to execute r synchronously. void injectPostToHandler(Runnable r) { mHandler.post(r); @@ -1681,6 +1700,7 @@ public class ShortcutService extends IShortcutService.Stub { verifyCaller(packageName, userId); final List newShortcuts = (List) shortcutInfoList.getList(); + verifyShortcutInfoPackages(packageName, newShortcuts); final int size = newShortcuts.size(); synchronized (mLock) { @@ -1732,6 +1752,7 @@ public class ShortcutService extends IShortcutService.Stub { verifyCaller(packageName, userId); final List newShortcuts = (List) shortcutInfoList.getList(); + verifyShortcutInfoPackages(packageName, newShortcuts); final int size = newShortcuts.size(); synchronized (mLock) { @@ -1812,6 +1833,7 @@ public class ShortcutService extends IShortcutService.Stub { verifyCaller(packageName, userId); final List newShortcuts = (List) shortcutInfoList.getList(); + verifyShortcutInfoPackages(packageName, newShortcuts); final int size = newShortcuts.size(); synchronized (mLock) { @@ -1871,6 +1893,7 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkNotNull(shortcut); Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled"); verifyCaller(packageName, userId); + verifyShortcutInfoPackage(packageName, shortcut); final Intent ret; synchronized (mLock) { @@ -1892,6 +1915,7 @@ public class ShortcutService extends IShortcutService.Stub { private boolean requestPinItem(String packageName, int userId, ShortcutInfo shortcut, AppWidgetProviderInfo appWidget, Bundle extras, IntentSender resultIntent) { verifyCaller(packageName, userId); + verifyShortcutInfoPackage(packageName, shortcut); final boolean ret; synchronized (mLock) { -- cgit v1.2.3 From eabaff1c7f02906e568997bdd7dc43006655387e Mon Sep 17 00:00:00 2001 From: Arthur Ishiguro Date: Mon, 25 Jun 2018 11:31:33 -0700 Subject: Resolve inconsistent parcel read in NanoAppFilter Bug: 77599679 Test: Compile only Change-Id: Ib417a5cb4d51744442d2fb14437cabbe5fd1c266 (cherry picked from commit abe5a73a4a81e312a1690fbc10a6b99ce98b699a) --- core/java/android/hardware/location/NanoAppFilter.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/java/android/hardware/location/NanoAppFilter.java b/core/java/android/hardware/location/NanoAppFilter.java index bf35a3d6fbd6..c44094908f3c 100644 --- a/core/java/android/hardware/location/NanoAppFilter.java +++ b/core/java/android/hardware/location/NanoAppFilter.java @@ -83,7 +83,7 @@ public class NanoAppFilter { mAppId = in.readLong(); mAppVersion = in.readInt(); mVersionRestrictionMask = in.readInt(); - mAppIdVendorMask = in.readInt(); + mAppIdVendorMask = in.readLong(); } public int describeContents() { @@ -91,7 +91,6 @@ public class NanoAppFilter { } public void writeToParcel(Parcel out, int flags) { - out.writeLong(mAppId); out.writeInt(mAppVersion); out.writeInt(mVersionRestrictionMask); -- cgit v1.2.3 From 7d8ce934a98791313f9a62d5f0196d0dec03eaf6 Mon Sep 17 00:00:00 2001 From: Bernie Innocenti Date: Mon, 28 May 2018 22:04:37 +0900 Subject: vpn: allow IPSec traffic through Always-on VPN This won't leak any traffic outside the VPN as long as there are no processes owned by uid 0 which generate network traffic (which is currently the case). Bug: 69873852 Test: compared the output of 'adb shell ip rule show' before and after Test: runtest -x frameworks/base/tests/net/java/com/android/server/connectivity/VpnTest.java Test: local CTS tests run: android.net.cts.VpnServiceTest Test: local CTS tests run: com.android.cts.devicepolicy.MixedDeviceOwnerTest Change-Id: I8758e576c9d961d73f62bfcf0559dd7ecee6e8e6 Merged-In: I8758e576c9d961d73f62bfcf0559dd7ecee6e8e6 Merged-In: I1f9b78c8f828ec2df7aba71b39d62be0c4db2550 Merged-In: I8edeb0942e661c8385ff0cd3fdb72e6f62a8f218 (cherry picked from commit 00000fe55a4729f8339afdc7eab5c970b2549813) (cherry picked from commit ef2910dc709d698b6476e8d462c608d04c784a26) --- .../core/java/com/android/server/connectivity/Vpn.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 56cff7c715d6..d51e1f738659 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -94,8 +94,6 @@ import com.android.server.DeviceIdleController; import com.android.server.LocalServices; import com.android.server.net.BaseNetworkObserver; -import libcore.io.IoUtils; - import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -114,6 +112,8 @@ import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.atomic.AtomicInteger; +import libcore.io.IoUtils; + /** * @hide */ @@ -1184,6 +1184,18 @@ public class Vpn { /* allowedApplications */ null, /* disallowedApplications */ exemptedPackages); + // The UID range of the first user (0-99999) would block the IPSec traffic, which comes + // directly from the kernel and is marked as uid=0. So we adjust the range to allow + // it through (b/69873852). + for (UidRange range : addedRanges) { + if (range.start == 0) { + addedRanges.remove(range); + if (range.stop != 0) { + addedRanges.add(new UidRange(1, range.stop)); + } + } + } + removedRanges.removeAll(addedRanges); addedRanges.removeAll(mBlockedUsers); } -- cgit v1.2.3 From f28568c95f0624d61a1e8d48522b49a5e14b77c4 Mon Sep 17 00:00:00 2001 From: Robert Shih Date: Mon, 9 Jul 2018 13:38:31 -0700 Subject: Fix TrackInfo parcel write Bug: 77600398 Change-Id: Ia316f1c5dc4879f6851fdb78fe8b9039579be7bc (cherry picked from commit 0d2dc943dcaa3d7c8479e22ae62be9753ea2643c) --- media/java/android/media/MediaPlayer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 0d99473c69b9..f384ea56ca21 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -2363,10 +2363,10 @@ public class MediaPlayer extends PlayerBase @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mTrackType); + dest.writeString(mFormat.getString(MediaFormat.KEY_MIME)); dest.writeString(getLanguage()); if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) { - dest.writeString(mFormat.getString(MediaFormat.KEY_MIME)); dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_AUTOSELECT)); dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_DEFAULT)); dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE)); -- cgit v1.2.3