Skip to content

Commit

Permalink
Merge tag 'android-15.0.0_r5' into staging/lineage-22.0_merge-android…
Browse files Browse the repository at this point in the history
…-15.0.0_r5

Android 15.0.0 release 5

# -----BEGIN PGP SIGNATURE-----
#
# iF0EABECAB0WIQRDQNE1cO+UXoOBCWTorT+BmrEOeAUCZyvenQAKCRDorT+BmrEO
# eMv6AJ9R1HOHNYlB4t3eC//tQgLxHGrjygCaA7xwa1Ychbn4tsWPdfSWGuE9ioA=
# =e7Fo
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed Nov  6 23:24:45 2024 EET
# gpg:                using DSA key 4340D13570EF945E83810964E8AD3F819AB10E78
# gpg: Good signature from "The Android Open Source Project <[email protected]>" [marginal]
# gpg: [email protected]: Verified 2332 signatures in the past
#      3 years.  Encrypted 4 messages in the past 2 years.
# gpg: WARNING: This key is not certified with sufficiently trusted signatures!
# gpg:          It is not certain that the signature belongs to the owner.
# Primary key fingerprint: 4340 D135 70EF 945E 8381  0964 E8AD 3F81 9AB1 0E78

# By Adam Bookatz (1) and others
# Via Android Build Coastguard Worker
* tag 'android-15.0.0_r5':
  Stops hiding a11y services with the same package+label as an activity.
  Only check INTERACT_ACROSS_USERS_FULL when user handle is not current
  Checks cross user permission before handling intent
  startActivityForResult with new Intent

Change-Id: I8d32037caafcf1bbb560a24dbf44f0aa68799e07
  • Loading branch information
mikeNG committed Nov 7, 2024
2 parents 400176f + a915284 commit 86aae56
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,13 @@
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ServiceInfo;
import android.hardware.input.InputManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Pair;
import android.view.InputDevice;
import android.view.accessibility.AccessibilityManager;

Expand Down Expand Up @@ -57,8 +55,6 @@
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/** Activity with the accessibility settings. */
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
Expand Down Expand Up @@ -458,20 +454,9 @@ private List<RestrictedPreference> getInstalledAccessibilityList(Context context
UserHandle.myUserId());
final List<AccessibilityActivityPreference> activityList =
preferenceHelper.createAccessibilityActivityPreferenceList(installedShortcutList);
final Set<Pair<String, CharSequence>> packageLabelPairs =
activityList.stream()
.map(a11yActivityPref -> new Pair<>(
a11yActivityPref.getPackageName(), a11yActivityPref.getLabel())
).collect(Collectors.toSet());

// Remove duplicate item here, new a ArrayList to copy unmodifiable list result
// (getInstalledAccessibilityServiceList).

final List<AccessibilityServiceInfo> installedServiceList = new ArrayList<>(
a11yManager.getInstalledAccessibilityServiceList());
if (!packageLabelPairs.isEmpty()) {
installedServiceList.removeIf(
target -> containsPackageAndLabelInList(packageLabelPairs, target));
}
final List<RestrictedPreference> serviceList =
preferenceHelper.createAccessibilityServicePreferenceList(installedServiceList);

Expand All @@ -482,16 +467,6 @@ private List<RestrictedPreference> getInstalledAccessibilityList(Context context
return preferenceList;
}

private boolean containsPackageAndLabelInList(
Set<Pair<String, CharSequence>> packageLabelPairs,
AccessibilityServiceInfo targetServiceInfo) {
final ServiceInfo serviceInfo = targetServiceInfo.getResolveInfo().serviceInfo;
final String servicePackageName = serviceInfo.packageName;
final CharSequence serviceLabel = serviceInfo.loadLabel(getPackageManager());

return packageLabelPairs.contains(new Pair<>(servicePackageName, serviceLabel));
}

private void initializePreBundledServicesMapFromArray(String categoryKey, int key) {
String[] services = getResources().getStringArray(key);
PreferenceCategory category = mCategoryToPrefCategoryMap.get(categoryKey);
Expand Down
33 changes: 31 additions & 2 deletions src/com/android/settings/applications/AppInfoBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;

import android.Manifest;
import android.app.Activity;
import android.app.Dialog;
import android.app.admin.DevicePolicyManager;
Expand All @@ -39,6 +40,7 @@
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
Expand Down Expand Up @@ -135,8 +137,13 @@ protected String retrieveAppEntry() {
}
}
if (intent != null && intent.hasExtra(Intent.EXTRA_USER_HANDLE)) {
mUserId = ((UserHandle) intent.getParcelableExtra(
Intent.EXTRA_USER_HANDLE)).getIdentifier();
mUserId = ((UserHandle) intent.getParcelableExtra(Intent.EXTRA_USER_HANDLE))
.getIdentifier();
if (mUserId != UserHandle.myUserId() && !hasInteractAcrossUsersFullPermission()) {
Log.w(TAG, "Intent not valid.");
finish();
return "";
}
} else {
mUserId = UserHandle.myUserId();
}
Expand All @@ -163,6 +170,28 @@ protected String retrieveAppEntry() {
return mPackageName;
}

@VisibleForTesting
protected boolean hasInteractAcrossUsersFullPermission() {
Activity activity = getActivity();
if (!(activity instanceof SettingsActivity)) {
return false;
}
final String callingPackageName =
((SettingsActivity) activity).getInitialCallingPackage();

if (TextUtils.isEmpty(callingPackageName)) {
Log.w(TAG, "Not able to get calling package name for permission check");
return false;
}
if (mPm.checkPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingPackageName)
!= PackageManager.PERMISSION_GRANTED) {
Log.w(TAG, "Package " + callingPackageName + " does not have required permission "
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL);
return false;
}
return true;
}

protected void setIntentAndFinish(boolean appChanged) {
Log.i(TAG, "appChanged=" + appChanged);
Intent intent = new Intent();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ public void onReceive(Context context, Intent intent) {
int requestCode = generateCustomActivityRequestCode(
RestrictionsResultReceiver.this.preference);
AppRestrictionsFragment.this.startActivityForResult(
restrictionsIntent, requestCode);
new Intent(restrictionsIntent), requestCode);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
Expand All @@ -50,6 +51,7 @@
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.testutils.XmlTestUtils;
import com.android.settings.testutils.shadow.ShadowAccessibilityManager;
import com.android.settings.testutils.shadow.ShadowApplicationPackageManager;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
Expand All @@ -75,7 +77,6 @@
import org.robolectric.android.controller.ActivityController;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowAccessibilityManager;
import org.robolectric.shadows.ShadowContentResolver;
import org.xmlpull.v1.XmlPullParserException;

Expand All @@ -87,6 +88,7 @@
/** Test for {@link AccessibilitySettings}. */
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {
ShadowAccessibilityManager.class,
ShadowBluetoothAdapter.class,
ShadowUserManager.class,
ShadowColorDisplayManager.class,
Expand All @@ -95,8 +97,10 @@
})
public class AccessibilitySettingsTest {
private static final String PACKAGE_NAME = "com.android.test";
private static final String CLASS_NAME = PACKAGE_NAME + ".test_a11y_service";
private static final ComponentName COMPONENT_NAME = new ComponentName(PACKAGE_NAME, CLASS_NAME);
private static final ComponentName SERVICE_COMPONENT_NAME =
new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".test_a11y_service");
private static final ComponentName ACTIVITY_COMPONENT_NAME =
new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".test_a11y_activity");
private static final String EMPTY_STRING = "";
private static final String DEFAULT_SUMMARY = "default summary";
private static final String DEFAULT_DESCRIPTION = "default description";
Expand All @@ -110,9 +114,7 @@ public class AccessibilitySettingsTest {
private final Context mContext = ApplicationProvider.getApplicationContext();
@Spy
private final AccessibilityServiceInfo mServiceInfo = getMockAccessibilityServiceInfo(
PACKAGE_NAME, CLASS_NAME);
@Mock
private AccessibilityShortcutInfo mShortcutInfo;
SERVICE_COMPONENT_NAME);
private ShadowAccessibilityManager mShadowAccessibilityManager;
@Mock
private LocalBluetoothManager mLocalBluetoothManager;
Expand All @@ -121,11 +123,11 @@ public class AccessibilitySettingsTest {

@Before
public void setup() {
mShadowAccessibilityManager = Shadow.extract(AccessibilityManager.getInstance(mContext));
mShadowAccessibilityManager = Shadow.extract(
mContext.getSystemService(AccessibilityManager.class));
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(new ArrayList<>());
mContext.setTheme(androidx.appcompat.R.style.Theme_AppCompat);
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
setMockAccessibilityShortcutInfo(mShortcutInfo);

Intent intent = new Intent();
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT,
Expand Down Expand Up @@ -368,7 +370,7 @@ public void onContentChanged_updatePreferenceInForeground_preferenceUpdated() {
mFragment.onContentChanged();

RestrictedPreference preference = mFragment.getPreferenceScreen().findPreference(
COMPONENT_NAME.flattenToString());
SERVICE_COMPONENT_NAME.flattenToString());

assertThat(preference).isNotNull();

Expand All @@ -388,7 +390,7 @@ public void onContentChanged_updatePreferenceInBackground_preferenceUpdated() {
mFragment.onResume();

RestrictedPreference preference = mFragment.getPreferenceScreen().findPreference(
COMPONENT_NAME.flattenToString());
SERVICE_COMPONENT_NAME.flattenToString());

assertThat(preference).isNotNull();

Expand Down Expand Up @@ -418,18 +420,44 @@ public void testAccessibilityMenuInSystem_NoPrefWhenNotInstalled() {
assertThat(pref).isNull();
}

private AccessibilityServiceInfo getMockAccessibilityServiceInfo(String packageName,
String className) {
return getMockAccessibilityServiceInfo(new ComponentName(packageName, className));
@Test
public void testSameNamedServiceAndActivity_bothPreferencesExist() {
final PackageManager pm = mContext.getPackageManager();
AccessibilityServiceInfo a11yServiceInfo = mServiceInfo;
AccessibilityShortcutInfo a11yShortcutInfo = getMockAccessibilityShortcutInfo();
// Ensure the test service and activity have the same package name and label.
// Before this change, any service and activity with the same package name and
// label would cause the service to be hidden.
assertThat(a11yServiceInfo.getComponentName())
.isNotEqualTo(a11yShortcutInfo.getComponentName());
assertThat(a11yServiceInfo.getComponentName().getPackageName())
.isEqualTo(a11yShortcutInfo.getComponentName().getPackageName());
assertThat(a11yServiceInfo.getResolveInfo().serviceInfo.loadLabel(pm))
.isEqualTo(a11yShortcutInfo.getActivityInfo().loadLabel(pm));
// Prepare A11yManager with the test service and activity.
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
List.of(mServiceInfo));
mShadowAccessibilityManager.setInstalledAccessibilityShortcutListAsUser(
List.of(getMockAccessibilityShortcutInfo()));
setupFragment();

// Both service and activity preferences should exist on the page.
RestrictedPreference servicePref = mFragment.getPreferenceScreen().findPreference(
a11yServiceInfo.getComponentName().flattenToString());
RestrictedPreference activityPref = mFragment.getPreferenceScreen().findPreference(
a11yShortcutInfo.getComponentName().flattenToString());
assertThat(servicePref).isNotNull();
assertThat(activityPref).isNotNull();
}

private AccessibilityServiceInfo getMockAccessibilityServiceInfo(ComponentName componentName) {
final ApplicationInfo applicationInfo = new ApplicationInfo();
final ServiceInfo serviceInfo = new ServiceInfo();
final ApplicationInfo applicationInfo = Mockito.mock(ApplicationInfo.class);
final ServiceInfo serviceInfo = Mockito.spy(new ServiceInfo());
applicationInfo.packageName = componentName.getPackageName();
serviceInfo.packageName = componentName.getPackageName();
serviceInfo.name = componentName.getClassName();
serviceInfo.applicationInfo = applicationInfo;
when(serviceInfo.loadLabel(any())).thenReturn(DEFAULT_LABEL);

final ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.serviceInfo = serviceInfo;
Expand All @@ -445,14 +473,16 @@ private AccessibilityServiceInfo getMockAccessibilityServiceInfo(ComponentName c
return null;
}

private void setMockAccessibilityShortcutInfo(AccessibilityShortcutInfo mockInfo) {
private AccessibilityShortcutInfo getMockAccessibilityShortcutInfo() {
AccessibilityShortcutInfo mockInfo = Mockito.mock(AccessibilityShortcutInfo.class);
final ActivityInfo activityInfo = Mockito.mock(ActivityInfo.class);
activityInfo.applicationInfo = new ApplicationInfo();
when(mockInfo.getActivityInfo()).thenReturn(activityInfo);
when(activityInfo.loadLabel(any())).thenReturn(DEFAULT_LABEL);
when(mockInfo.loadSummary(any())).thenReturn(DEFAULT_SUMMARY);
when(mockInfo.loadDescription(any())).thenReturn(DEFAULT_DESCRIPTION);
when(mockInfo.getComponentName()).thenReturn(COMPONENT_NAME);
when(mockInfo.getComponentName()).thenReturn(ACTIVITY_COMPONENT_NAME);
return mockInfo;
}

private void setInvisibleToggleFragmentType(AccessibilityServiceInfo info) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,32 @@ public void extraUserHandleInIntent_retrieveAppEntryWithMyUserId()
assertThat(mAppInfoWithHeader.mAppEntry).isNotNull();
}

@Test
public void noCrossUserPermission_retrieveAppEntry_fail()
throws PackageManager.NameNotFoundException {
TestFragmentWithoutPermission testFragmentWithoutPermission =
new TestFragmentWithoutPermission();
final int userId = 1002;
final String packageName = "com.android.settings";

testFragmentWithoutPermission.mIntent.putExtra(Intent.EXTRA_USER_HANDLE,
new UserHandle(userId));
testFragmentWithoutPermission.mIntent.setData(Uri.fromParts("package",
packageName, null));
final ApplicationsState.AppEntry entry = mock(ApplicationsState.AppEntry.class);
entry.info = new ApplicationInfo();
entry.info.packageName = packageName;

when(testFragmentWithoutPermission.mState.getEntry(packageName, userId)).thenReturn(entry);
when(testFragmentWithoutPermission.mPm.getPackageInfoAsUser(eq(entry.info.packageName),
any(), eq(userId))).thenReturn(
testFragmentWithoutPermission.mPackageInfo);

testFragmentWithoutPermission.retrieveAppEntry();

assertThat(testFragmentWithoutPermission.mAppEntry).isNull();
}

public static class TestFragment extends AppInfoWithHeader {

PreferenceManager mManager;
Expand Down Expand Up @@ -223,6 +249,11 @@ public Context getContext() {
return mShadowContext;
}

@Override
protected boolean hasInteractAcrossUsersFullPermission() {
return true;
}

@Override
protected void onPackageRemoved() {
mPackageRemovedCalled = true;
Expand All @@ -233,4 +264,11 @@ protected Intent getIntent() {
return mIntent;
}
}

private static final class TestFragmentWithoutPermission extends TestFragment {
@Override
protected boolean hasInteractAcrossUsersFullPermission() {
return false;
}
}
}
Loading

0 comments on commit 86aae56

Please sign in to comment.