Merge "Settings 2-pane deep link vulnerabilities" into tm-dev
diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java
index 8e14c5a..467f0aa 100644
--- a/src/com/android/settings/homepage/SettingsHomepageActivity.java
+++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java
@@ -27,6 +27,8 @@
 import android.app.settings.SettingsEnums;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.os.Bundle;
@@ -43,6 +45,7 @@
 import android.widget.ImageView;
 import android.widget.Toolbar;
 
+import androidx.annotation.VisibleForTesting;
 import androidx.core.graphics.Insets;
 import androidx.core.view.ViewCompat;
 import androidx.core.view.WindowCompat;
@@ -65,6 +68,7 @@
 import com.android.settings.core.FeatureFlags;
 import com.android.settings.homepage.contextualcards.ContextualCardsFragment;
 import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.password.PasswordUtils;
 import com.android.settingslib.Utils;
 import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin;
 
@@ -431,6 +435,32 @@
             finish();
             return;
         }
+
+        if (!TextUtils.equals(PasswordUtils.getCallingAppPackageName(getActivityToken()),
+                getPackageName())) {
+            ActivityInfo targetActivityInfo = null;
+            try {
+                targetActivityInfo = getPackageManager().getActivityInfo(targetComponentName,
+                        /* flags= */ 0);
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.e(TAG, "Failed to get target ActivityInfo: " + e);
+                finish();
+                return;
+            }
+
+            if (!targetActivityInfo.exported) {
+                Log.e(TAG, "Must not launch an unexported Actvity for deep link");
+                finish();
+                return;
+            }
+
+            if (!isCallingAppPermitted(targetActivityInfo.permission)) {
+                Log.e(TAG, "Calling app must have the permission of deep link Activity");
+                finish();
+                return;
+            }
+        }
+
         targetIntent.setComponent(targetComponentName);
 
         // To prevent launchDeepLinkIntentToRight again for configuration change.
@@ -472,6 +502,12 @@
         }
     }
 
+    @VisibleForTesting
+    boolean isCallingAppPermitted(String permission) {
+        return TextUtils.isEmpty(permission) || PasswordUtils.isCallingAppPermitted(
+                this, getActivityToken(), permission);
+    }
+
     private String getHighlightMenuKey() {
         final Intent intent = getIntent();
         if (intent != null && TextUtils.equals(intent.getAction(),
diff --git a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java b/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java
index 4d203a8..4de8b00 100644
--- a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java
@@ -20,6 +20,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -37,9 +39,11 @@
 import com.android.settings.R;
 import com.android.settings.dashboard.suggestions.SuggestionFeatureProviderImpl;
 import com.android.settings.homepage.contextualcards.slices.BatteryFixSliceTest;
+import com.android.settings.testutils.shadow.ShadowPasswordUtils;
 import com.android.settings.testutils.shadow.ShadowUserManager;
 import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -66,6 +70,11 @@
         MockitoAnnotations.initMocks(this);
     }
 
+    @After
+    public void tearDown() {
+        ShadowPasswordUtils.reset();
+    }
+
     @Test
     public void launch_shouldHaveAnimationForIaFragment() {
         final SettingsHomepageActivity activity = Robolectric.buildActivity(
@@ -195,6 +204,32 @@
                 & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS).isEqualTo(0);
     }
 
+    @Test
+    @Config(shadows = {ShadowPasswordUtils.class})
+    public void isCallingAppPermitted_emptyPermission_returnTrue() {
+        SettingsHomepageActivity homepageActivity = spy(new SettingsHomepageActivity());
+
+        assertTrue(homepageActivity.isCallingAppPermitted(""));
+    }
+
+    @Test
+    @Config(shadows = {ShadowPasswordUtils.class})
+    public void isCallingAppPermitted_noGrantedPermission_returnFalse() {
+        SettingsHomepageActivity homepageActivity = spy(new SettingsHomepageActivity());
+
+        assertFalse(homepageActivity.isCallingAppPermitted("android.permission.TEST"));
+    }
+
+    @Test
+    @Config(shadows = {ShadowPasswordUtils.class})
+    public void isCallingAppPermitted_grantedPermission_returnTrue() {
+        SettingsHomepageActivity homepageActivity = spy(new SettingsHomepageActivity());
+        String permission = "android.permission.TEST";
+        ShadowPasswordUtils.addGrantedPermission(permission);
+
+        assertTrue(homepageActivity.isCallingAppPermitted(permission));
+    }
+
     @Implements(SuggestionFeatureProviderImpl.class)
     public static class ShadowSuggestionFeatureProviderImpl {