summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--java/src/com/android/intentresolver/v2/ChooserActivity.java30
-rw-r--r--java/src/com/android/intentresolver/v2/ResolverActivity.java32
-rw-r--r--java/src/com/android/intentresolver/v2/ext/CreationExtrasExt.kt31
-rw-r--r--java/src/com/android/intentresolver/v2/ext/ParcelExt.kt27
-rw-r--r--java/src/com/android/intentresolver/v2/ui/model/ActivityLaunch.kt (renamed from java/src/com/android/intentresolver/v2/ui/model/CallerInfo.kt)46
-rw-r--r--java/src/com/android/intentresolver/v2/ui/model/ActivityLaunchModule.kt40
-rw-r--r--java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestReader.kt16
-rw-r--r--java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserViewModel.kt17
-rw-r--r--tests/activity/src/com/android/intentresolver/ChooserWrapperActivity.java7
-rw-r--r--tests/activity/src/com/android/intentresolver/v2/ChooserWrapperActivity.java7
-rw-r--r--tests/activity/src/com/android/intentresolver/v2/ui/model/TestActivityLaunchModule.kt41
-rw-r--r--tests/shared/src/com/android/intentresolver/v2/ext/ParcelableExt.kt45
-rw-r--r--tests/unit/src/com/android/intentresolver/v2/ui/model/ActivityLaunchTest.kt67
-rw-r--r--tests/unit/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestTest.kt23
14 files changed, 345 insertions, 84 deletions
diff --git a/java/src/com/android/intentresolver/v2/ChooserActivity.java b/java/src/com/android/intentresolver/v2/ChooserActivity.java
index 95d9ea18..c1184a80 100644
--- a/java/src/com/android/intentresolver/v2/ChooserActivity.java
+++ b/java/src/com/android/intentresolver/v2/ChooserActivity.java
@@ -29,6 +29,7 @@ import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTE
import static androidx.lifecycle.LifecycleKt.getCoroutineScope;
+import static com.android.intentresolver.v2.ext.CreationExtrasExtKt.addDefaultArgs;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;
import static com.android.internal.util.LatencyTracker.ACTION_LOAD_SHARE_SHEET;
@@ -75,7 +76,6 @@ import android.stats.devicepolicy.DevicePolicyEnums;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
-import android.util.SparseArray;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -95,7 +95,6 @@ import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
-import androidx.lifecycle.SavedStateHandleSupport;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.viewmodel.CreationExtras;
import androidx.recyclerview.widget.GridLayoutManager;
@@ -149,7 +148,7 @@ import com.android.intentresolver.v2.platform.AppPredictionAvailable;
import com.android.intentresolver.v2.platform.ImageEditor;
import com.android.intentresolver.v2.platform.NearbyShare;
import com.android.intentresolver.v2.ui.ActionTitle;
-import com.android.intentresolver.v2.ui.model.CallerInfo;
+import com.android.intentresolver.v2.ui.model.ActivityLaunch;
import com.android.intentresolver.v2.ui.model.ChooserRequest;
import com.android.intentresolver.v2.ui.viewmodel.ChooserViewModel;
import com.android.intentresolver.widget.ImagePreviewView;
@@ -164,6 +163,7 @@ import com.google.common.collect.ImmutableList;
import dagger.hilt.android.AndroidEntryPoint;
+import kotlin.Pair;
import kotlin.Unit;
import java.util.ArrayList;
@@ -265,6 +265,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
private static final int SCROLL_STATUS_SCROLLING_VERTICAL = 1;
private static final int SCROLL_STATUS_SCROLLING_HORIZONTAL = 2;
+ @Inject public ActivityLaunch mActivityLaunch;
@Inject public FeatureFlags mFeatureFlags;
@Inject public EventLog mEventLog;
@Inject @AppPredictionAvailable public boolean mAppPredictionAvailable;
@@ -332,20 +333,21 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
@NonNull
@Override
public CreationExtras getDefaultViewModelCreationExtras() {
- CreationExtras extras = super.getDefaultViewModelCreationExtras();
- // Inserts a CallerInfo into the Bundle at stored at DEFAULT_ARGS_KEY
- Bundle defaultArgs = requireNonNull(extras.get(SavedStateHandleSupport.DEFAULT_ARGS_KEY));
- defaultArgs.putParcelable(CallerInfo.SAVED_STATE_HANDLE_KEY,
- new CallerInfo(getLaunchedFromUid(),
- getLaunchedFromPackage(),
- requireNonNull(getReferrer())));
- return extras;
+ return addDefaultArgs(
+ super.getDefaultViewModelCreationExtras(),
+ new Pair<>(ActivityLaunch.ACTIVITY_LAUNCH_KEY, mActivityLaunch));
}
@Override
protected final void onCreate(Bundle savedInstanceState) {
- Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
+ Log.i(TAG, "onCreate");
+ Log.i(TAG, "activityLaunch=" + mActivityLaunch.toString());
+ int callerUid = mActivityLaunch.getFromUid();
+ if (callerUid < 0 || UserHandle.isIsolated(callerUid)) {
+ Log.e(TAG, "Can't start a resolver from uid " + callerUid);
+ finish();
+ }
setTheme(R.style.Theme_DeviceDefault_Chooser);
Tracer.INSTANCE.markLaunched();
mViewModel = new ViewModelProvider(this).get(ChooserViewModel.class);
@@ -824,7 +826,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
}
} catch (RuntimeException e) {
Slog.wtf(TAG,
- "Unable to launch as uid " + requireAnnotatedUserHandles().userIdOfCallingApp
+ "Unable to launch as uid " + mActivityLaunch.getFromUid()
+ " package " + getLaunchedFromPackage() + ", while running in "
+ ActivityThread.currentProcessName(), e);
}
@@ -2103,7 +2105,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
mPackageManager,
mLogic.getTargetIntent(),
mLogic.getReferrerPackageName(),
- requireAnnotatedUserHandles().userIdOfCallingApp,
+ mActivityLaunch.getFromUid(),
resolverComparator,
getQueryIntentsUser(userHandle));
}
diff --git a/java/src/com/android/intentresolver/v2/ResolverActivity.java b/java/src/com/android/intentresolver/v2/ResolverActivity.java
index 55e698a6..a308ea14 100644
--- a/java/src/com/android/intentresolver/v2/ResolverActivity.java
+++ b/java/src/com/android/intentresolver/v2/ResolverActivity.java
@@ -24,6 +24,7 @@ import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_S
import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static com.android.intentresolver.v2.ext.CreationExtrasExtKt.addDefaultArgs;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;
import static java.util.Collections.emptyList;
@@ -83,6 +84,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.viewmodel.CreationExtras;
import androidx.viewpager.widget.ViewPager;
import com.android.intentresolver.AnnotatedUserHandles;
@@ -109,6 +111,7 @@ import com.android.intentresolver.v2.emptystate.NoCrossProfileEmptyStateProvider
import com.android.intentresolver.v2.emptystate.WorkProfilePausedEmptyStateProvider;
import com.android.intentresolver.v2.ext.IntentExtKt;
import com.android.intentresolver.v2.ui.ActionTitle;
+import com.android.intentresolver.v2.ui.model.ActivityLaunch;
import com.android.intentresolver.widget.ResolverDrawerLayout;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
@@ -119,6 +122,7 @@ import com.google.common.collect.ImmutableList;
import dagger.hilt.android.AndroidEntryPoint;
+import kotlin.Pair;
import kotlin.Unit;
import java.util.ArrayList;
@@ -140,6 +144,7 @@ import javax.inject.Inject;
public class ResolverActivity extends Hilt_ResolverActivity implements
ResolverListAdapter.ResolverListCommunicator {
+ @Inject public ActivityLaunch mActivityLaunch;
@Inject public DevicePolicyResources mDevicePolicyResources;
@Inject public IntentForwarding mIntentForwarding;
@@ -235,10 +240,26 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
this::onWorkProfileStatusUpdated);
}
+ @NonNull
+ @Override
+ public CreationExtras getDefaultViewModelCreationExtras() {
+ return addDefaultArgs(
+ super.getDefaultViewModelCreationExtras(),
+ new Pair<>(ActivityLaunch.ACTIVITY_LAUNCH_KEY, mActivityLaunch));
+ }
+
@Override
protected final void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(R.style.Theme_DeviceDefault_Resolver);
+ Log.i(TAG, "onCreate");
+ Log.i(TAG, "activityLaunch=" + mActivityLaunch.toString());
+ int callerUid = mActivityLaunch.getFromUid();
+ if (callerUid < 0 || UserHandle.isIsolated(callerUid)) {
+ Log.e(TAG, "Can't start a resolver from uid " + callerUid);
+ finish();
+ }
+
mLogic = createActivityLogic();
mResolvingHome = IntentExtKt.isHomeIntent(getIntent());
mTargetDataLoader = new DefaultTargetDataLoader(
@@ -256,13 +277,6 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
Intent intent = mLogic.getTargetIntent();
List<Intent> initialIntents = mLogic.getInitialIntents();
- // Calling UID did not have valid permissions
- if (mLogic.getAnnotatedUserHandles() == null) {
- finish();
- return;
- }
-
-
// The last argument of createResolverListAdapter is whether to do special handling
// of the last used choice to highlight it in the list. We need to always
// turn this off when running under voice interaction, since it results in
@@ -760,7 +774,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
getPackageManager(),
mLogic.getTargetIntent(),
mLogic.getReferrerPackageName(),
- requireAnnotatedUserHandles().userIdOfCallingApp,
+ mActivityLaunch.getFromUid(),
resolverComparator,
getQueryIntentsUser(userHandle));
}
@@ -1486,7 +1500,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
}
} catch (RuntimeException e) {
Slog.wtf(TAG,
- "Unable to launch as uid " + requireAnnotatedUserHandles().userIdOfCallingApp
+ "Unable to launch as uid " + mActivityLaunch.getFromUid()
+ " package " + getLaunchedFromPackage() + ", while running in "
+ ActivityThread.currentProcessName(), e);
}
diff --git a/java/src/com/android/intentresolver/v2/ext/CreationExtrasExt.kt b/java/src/com/android/intentresolver/v2/ext/CreationExtrasExt.kt
new file mode 100644
index 00000000..ebd613f1
--- /dev/null
+++ b/java/src/com/android/intentresolver/v2/ext/CreationExtrasExt.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.intentresolver.v2.ext
+
+import android.os.Bundle
+import android.os.Parcelable
+import androidx.lifecycle.DEFAULT_ARGS_KEY
+import androidx.lifecycle.viewmodel.CreationExtras
+
+/** Adds one or more key-value pairs to the default Args bundle in this extras instance. */
+fun CreationExtras.addDefaultArgs(vararg values: Pair<String, Parcelable>): CreationExtras {
+ val defaultArgs: Bundle = get(DEFAULT_ARGS_KEY) ?: Bundle()
+ for ((key, value) in values) {
+ defaultArgs.putParcelable(key, value)
+ }
+ return this
+}
diff --git a/java/src/com/android/intentresolver/v2/ext/ParcelExt.kt b/java/src/com/android/intentresolver/v2/ext/ParcelExt.kt
new file mode 100644
index 00000000..b0ec97f4
--- /dev/null
+++ b/java/src/com/android/intentresolver/v2/ext/ParcelExt.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.intentresolver.v2.ext
+
+import android.os.Parcel
+
+inline fun <reified T> Parcel.requireParcelable(): T {
+ return requireNotNull(readParcelable<T>()) { "A non-value required from this parcel was null!" }
+}
+
+inline fun <reified T> Parcel.readParcelable(): T? {
+ return readParcelable(T::class.java.classLoader, T::class.java)
+}
diff --git a/java/src/com/android/intentresolver/v2/ui/model/CallerInfo.kt b/java/src/com/android/intentresolver/v2/ui/model/ActivityLaunch.kt
index 9addeef2..fd25ea42 100644
--- a/java/src/com/android/intentresolver/v2/ui/model/CallerInfo.kt
+++ b/java/src/com/android/intentresolver/v2/ui/model/ActivityLaunch.kt
@@ -15,45 +15,51 @@
*/
package com.android.intentresolver.v2.ui.model
+import android.content.Intent
import android.net.Uri
import android.os.Parcel
import android.os.Parcelable
+import com.android.intentresolver.v2.ext.readParcelable
+import com.android.intentresolver.v2.ext.requireParcelable
-data class CallerInfo(
- val launchedFromUid: Int,
- val launchedFomPackage: String?,
- /* logged to metrics, forwarded to outgoing intent */
- val referrer: Uri
+/** Contains Activity-scope information about the state at launch time. */
+data class ActivityLaunch(
+ /** The [Intent] received by the app */
+ val intent: Intent,
+ /** The identifier for the sending app and user */
+ val fromUid: Int,
+ /** The package of the sending app */
+ val fromPackage: String?,
+ /** The referrer as supplied to the activity. */
+ val referrer: Uri?
) : Parcelable {
constructor(
source: Parcel
) : this(
- launchedFromUid = source.readInt(),
- launchedFomPackage = source.readString(),
- checkNotNull(source.readParcelable())
+ intent = source.requireParcelable(),
+ fromUid = source.readInt(),
+ fromPackage = source.readString(),
+ referrer = source.readParcelable()
)
override fun describeContents() = 0 /* flags */
override fun writeToParcel(dest: Parcel, flags: Int) {
- dest.writeInt(launchedFromUid)
- dest.writeString(launchedFomPackage)
- dest.writeParcelable(referrer, 0)
+ dest.writeParcelable(intent, flags)
+ dest.writeInt(fromUid)
+ dest.writeString(fromPackage)
+ dest.writeParcelable(referrer, flags)
}
companion object {
- const val SAVED_STATE_HANDLE_KEY = "com.android.intentresolver.CALLER_INFO"
+ const val ACTIVITY_LAUNCH_KEY = "com.android.intentresolver.ACTIVITY_LAUNCH"
- @JvmStatic
+ @JvmField
@Suppress("unused")
val CREATOR =
- object : Parcelable.Creator<CallerInfo> {
- override fun newArray(size: Int) = arrayOfNulls<CallerInfo>(size)
- override fun createFromParcel(source: Parcel) = CallerInfo(source)
+ object : Parcelable.Creator<ActivityLaunch> {
+ override fun newArray(size: Int) = arrayOfNulls<ActivityLaunch>(size)
+ override fun createFromParcel(source: Parcel) = ActivityLaunch(source)
}
}
}
-
-inline fun <reified T> Parcel.readParcelable(): T? {
- return readParcelable(T::class.java.classLoader, T::class.java)
-}
diff --git a/java/src/com/android/intentresolver/v2/ui/model/ActivityLaunchModule.kt b/java/src/com/android/intentresolver/v2/ui/model/ActivityLaunchModule.kt
new file mode 100644
index 00000000..3311467e
--- /dev/null
+++ b/java/src/com/android/intentresolver/v2/ui/model/ActivityLaunchModule.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.intentresolver.v2.ui.model
+
+import android.app.Activity
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.components.ActivityComponent
+import dagger.hilt.android.scopes.ActivityScoped
+
+@Module
+@InstallIn(ActivityComponent::class)
+object ActivityLaunchModule {
+
+ @Provides
+ @ActivityScoped
+ fun callerInfo(activity: Activity): ActivityLaunch {
+ return ActivityLaunch(
+ activity.intent,
+ activity.launchedFromUid,
+ activity.launchedFromPackage,
+ activity.referrer
+ )
+ }
+}
diff --git a/java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestReader.kt b/java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestReader.kt
index 6878be5f..33868aaf 100644
--- a/java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestReader.kt
+++ b/java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestReader.kt
@@ -44,10 +44,11 @@ import com.android.intentresolver.R
import com.android.intentresolver.util.hasValidIcon
import com.android.intentresolver.v2.ext.hasAction
import com.android.intentresolver.v2.ext.ifMatch
-import com.android.intentresolver.v2.ui.model.CallerInfo
+import com.android.intentresolver.v2.ui.model.ActivityLaunch
import com.android.intentresolver.v2.ui.model.ChooserRequest
import com.android.intentresolver.v2.ui.model.MAX_CHOOSER_ACTIONS
import com.android.intentresolver.v2.ui.model.MAX_INITIAL_INTENTS
+import com.android.intentresolver.v2.validation.ValidationResult
import com.android.intentresolver.v2.validation.types.IntentOrUri
import com.android.intentresolver.v2.validation.types.array
import com.android.intentresolver.v2.validation.types.value
@@ -61,8 +62,10 @@ internal fun Intent.maybeAddSendActionFlags() =
addFlags(FLAG_ACTIVITY_MULTIPLE_TASK)
}
-fun readChooserRequest(callerInfo: CallerInfo, source: (String) -> Any?) =
- validateFrom(source) {
+fun readChooserRequest(launch: ActivityLaunch): ValidationResult<ChooserRequest> {
+ val extras = launch.intent.extras ?: Bundle()
+ @Suppress("DEPRECATION")
+ return validateFrom(extras::get) {
val targetIntent = required(IntentOrUri(EXTRA_INTENT)).maybeAddSendActionFlags()
val isSendAction = targetIntent.hasAction(ACTION_SEND, ACTION_SEND_MULTIPLE)
@@ -118,7 +121,7 @@ fun readChooserRequest(callerInfo: CallerInfo, source: (String) -> Any?) =
val modifyShareAction = optional(value<ChooserAction>(EXTRA_CHOOSER_MODIFY_SHARE_ACTION))
- val referrerFillIn = Intent().putExtra(EXTRA_REFERRER, callerInfo.referrer)
+ val referrerFillIn = Intent().putExtra(EXTRA_REFERRER, launch.referrer)
ChooserRequest(
targetIntent = targetIntent,
@@ -126,8 +129,8 @@ fun readChooserRequest(callerInfo: CallerInfo, source: (String) -> Any?) =
isSendActionTarget = isSendAction,
targetType = targetIntent.type,
launchedFromPackage =
- requireNotNull(callerInfo.launchedFomPackage) {
- "launchedFromPackage was null, See Activity.getLaunchedFromPackage()"
+ requireNotNull(launch.fromPackage) {
+ "launch.fromPackage was null, See Activity.getLaunchedFromPackage()"
},
title = customTitle,
defaultTitleResource = defaultTitleResource,
@@ -146,6 +149,7 @@ fun readChooserRequest(callerInfo: CallerInfo, source: (String) -> Any?) =
shareTargetFilter = targetIntent.toShareTargetFilter()
)
}
+}
private fun Intent.toShareTargetFilter(): IntentFilter? {
return type?.let {
diff --git a/java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserViewModel.kt b/java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserViewModel.kt
index 663235ca..17b1e664 100644
--- a/java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserViewModel.kt
+++ b/java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserViewModel.kt
@@ -18,7 +18,8 @@ package com.android.intentresolver.v2.ui.viewmodel
import android.util.Log
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
-import com.android.intentresolver.v2.ui.model.CallerInfo
+import com.android.intentresolver.v2.ui.model.ActivityLaunch
+import com.android.intentresolver.v2.ui.model.ActivityLaunch.Companion.ACTIVITY_LAUNCH_KEY
import com.android.intentresolver.v2.ui.model.ChooserRequest
import com.android.intentresolver.v2.validation.ValidationResult
import dagger.hilt.android.lifecycle.HiltViewModel
@@ -27,19 +28,15 @@ import javax.inject.Inject
private const val TAG = "ChooserViewModel"
@HiltViewModel
-class ChooserViewModel
-@Inject
-constructor(
- private val args: SavedStateHandle,
-) : ViewModel() {
+class ChooserViewModel @Inject constructor(args: SavedStateHandle) : ViewModel() {
- private val callerInfo: CallerInfo =
- requireNotNull(args[CallerInfo.SAVED_STATE_HANDLE_KEY]) {
- "CallerInfo missing in SavedStateHandle! (${CallerInfo.SAVED_STATE_HANDLE_KEY})"
+ private val mActivityLaunch: ActivityLaunch =
+ requireNotNull(args[ACTIVITY_LAUNCH_KEY]) {
+ "ActivityLaunch missing in SavedStateHandle! ($ACTIVITY_LAUNCH_KEY)"
}
/** The result of reading and validating the inputs provided in savedState. */
- private val status: ValidationResult<ChooserRequest> = readChooserRequest(callerInfo, args::get)
+ private val status: ValidationResult<ChooserRequest> = readChooserRequest(mActivityLaunch)
val chooserRequest: ChooserRequest by lazy { status.getOrThrow() }
diff --git a/tests/activity/src/com/android/intentresolver/ChooserWrapperActivity.java b/tests/activity/src/com/android/intentresolver/ChooserWrapperActivity.java
index c0121f2e..37bbc6ce 100644
--- a/tests/activity/src/com/android/intentresolver/ChooserWrapperActivity.java
+++ b/tests/activity/src/com/android/intentresolver/ChooserWrapperActivity.java
@@ -54,13 +54,6 @@ public class ChooserWrapperActivity extends ChooserActivity implements IChooserW
static final ChooserActivityOverrideData sOverrides = ChooserActivityOverrideData.getInstance();
private UsageStatsManager mUsm;
- // ResolverActivity (the base class of ChooserActivity) inspects the launched-from UID at
- // onCreate and needs to see some non-negative value in the test.
- @Override
- public int getLaunchedFromUid() {
- return 1234;
- }
-
@Override
public ChooserListAdapter createChooserListAdapter(
Context context,
diff --git a/tests/activity/src/com/android/intentresolver/v2/ChooserWrapperActivity.java b/tests/activity/src/com/android/intentresolver/v2/ChooserWrapperActivity.java
index e7c8cce3..64c8e49d 100644
--- a/tests/activity/src/com/android/intentresolver/v2/ChooserWrapperActivity.java
+++ b/tests/activity/src/com/android/intentresolver/v2/ChooserWrapperActivity.java
@@ -65,13 +65,6 @@ public class ChooserWrapperActivity extends ChooserActivity implements IChooserW
sOverrides.mWorkProfileAvailability);
}
- // ResolverActivity (the base class of ChooserActivity) inspects the launched-from UID at
- // onCreate and needs to see some non-negative value in the test.
- @Override
- public int getLaunchedFromUid() {
- return 1234;
- }
-
@Override
public ChooserListAdapter createChooserListAdapter(
Context context,
diff --git a/tests/activity/src/com/android/intentresolver/v2/ui/model/TestActivityLaunchModule.kt b/tests/activity/src/com/android/intentresolver/v2/ui/model/TestActivityLaunchModule.kt
new file mode 100644
index 00000000..d674bbc2
--- /dev/null
+++ b/tests/activity/src/com/android/intentresolver/v2/ui/model/TestActivityLaunchModule.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.intentresolver.v2.ui.model
+
+import android.app.Activity
+import android.net.Uri
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.android.components.ActivityComponent
+import dagger.hilt.android.scopes.ActivityScoped
+import dagger.hilt.testing.TestInstallIn
+
+@Module
+@TestInstallIn(components = [ActivityComponent::class], replaces = [ActivityLaunchModule::class])
+class TestActivityLaunchModule {
+
+ @Provides
+ @ActivityScoped
+ fun activityLaunch(activity: Activity): ActivityLaunch {
+ return ActivityLaunch(activity.intent, LAUNCHED_FROM_UID, LAUNCHED_FROM_PACKAGE, REFERRER)
+ }
+
+ companion object {
+ const val LAUNCHED_FROM_PACKAGE = "example.com"
+ const val LAUNCHED_FROM_UID = 1234
+ val REFERRER: Uri = Uri.parse("android-app://$LAUNCHED_FROM_PACKAGE")
+ }
+}
diff --git a/tests/shared/src/com/android/intentresolver/v2/ext/ParcelableExt.kt b/tests/shared/src/com/android/intentresolver/v2/ext/ParcelableExt.kt
new file mode 100644
index 00000000..3878c39c
--- /dev/null
+++ b/tests/shared/src/com/android/intentresolver/v2/ext/ParcelableExt.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.intentresolver.v2.ext
+
+import android.os.Parcel
+import android.os.Parcelable
+import java.lang.reflect.Field
+
+inline fun <reified T : Parcelable> T.toParcelAndBack(): T {
+ val creator: Parcelable.Creator<out T> = getCreator()
+ val parcel = Parcel.obtain()
+ writeToParcel(parcel, 0)
+ parcel.setDataPosition(0)
+ return creator.createFromParcel(parcel)
+}
+
+inline fun <reified T : Parcelable> getCreator(): Parcelable.Creator<out T> {
+ return getCreator(T::class.java)
+}
+
+inline fun <reified T : Parcelable> getCreator(clazz: Class<out T>): Parcelable.Creator<out T> {
+ return try {
+ val field: Field = clazz.getDeclaredField("CREATOR")
+ @Suppress("UNCHECKED_CAST")
+ field.get(null) as Parcelable.Creator<T>
+ } catch (e: NoSuchFieldException) {
+ error("$clazz is a Parcelable without CREATOR")
+ } catch (e: IllegalAccessException) {
+ error("CREATOR in $clazz::class is not accessible")
+ }
+}
diff --git a/tests/unit/src/com/android/intentresolver/v2/ui/model/ActivityLaunchTest.kt b/tests/unit/src/com/android/intentresolver/v2/ui/model/ActivityLaunchTest.kt
new file mode 100644
index 00000000..3e9f43da
--- /dev/null
+++ b/tests/unit/src/com/android/intentresolver/v2/ui/model/ActivityLaunchTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.intentresolver.v2.ui.model
+
+import android.content.Intent
+import android.content.Intent.ACTION_CHOOSER
+import android.content.Intent.EXTRA_TEXT
+import android.net.Uri
+import com.android.intentresolver.v2.ext.toParcelAndBack
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Test
+
+class ActivityLaunchTest {
+
+ @Test
+ fun testDefaultValues() {
+ val input = ActivityLaunch(Intent(ACTION_CHOOSER), 0, null, null)
+
+ val output = input.toParcelAndBack()
+
+ assertEquals(input, output)
+ }
+
+ @Test
+ fun testCommonValues() {
+ val intent = Intent(ACTION_CHOOSER).apply { putExtra(EXTRA_TEXT, "Test") }
+ val input =
+ ActivityLaunch(intent, 1234, "com.example", Uri.parse("android-app://example.com"))
+
+ val output = input.toParcelAndBack()
+
+ assertEquals(input, output)
+ }
+
+ fun assertEquals(expected: ActivityLaunch, actual: ActivityLaunch) {
+ // Test fields separately: Intent does not override equals()
+ assertWithMessage("%s.filterEquals(%s)", actual.intent, expected.intent)
+ .that(actual.intent.filterEquals(expected.intent))
+ .isTrue()
+
+ assertWithMessage("actual fromUid is equal to expected")
+ .that(actual.fromUid)
+ .isEqualTo(expected.fromUid)
+
+ assertWithMessage("actual fromPackage is equal to expected")
+ .that(actual.fromPackage)
+ .isEqualTo(expected.fromPackage)
+
+ assertWithMessage("actual referrer is equal to expected")
+ .that(actual.referrer)
+ .isEqualTo(expected.referrer)
+ }
+}
diff --git a/tests/unit/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestTest.kt b/tests/unit/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestTest.kt
index bcc1054c..29bb5cbd 100644
--- a/tests/unit/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestTest.kt
+++ b/tests/unit/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestTest.kt
@@ -16,11 +16,12 @@
package com.android.intentresolver.v2.ui.viewmodel
import android.content.Intent
+import android.content.Intent.ACTION_CHOOSER
import android.content.Intent.ACTION_SEND
import android.content.Intent.EXTRA_INTENT
import androidx.core.net.toUri
import androidx.core.os.bundleOf
-import com.android.intentresolver.v2.ui.model.CallerInfo
+import com.android.intentresolver.v2.ui.model.ActivityLaunch
import com.android.intentresolver.v2.ui.model.ChooserRequest
import com.android.intentresolver.v2.validation.RequiredValueMissing
import com.android.intentresolver.v2.validation.ValidationResultSubject.Companion.assertThat
@@ -30,18 +31,18 @@ import org.junit.Test
@Suppress("DEPRECATION")
class ChooserRequestTest {
- private val callerInfo =
- CallerInfo(
- launchedFromUid = 10000,
- launchedFomPackage = "com.android.example",
+ val intent = Intent(ACTION_CHOOSER)
+ private val mActivityLaunch =
+ ActivityLaunch(
+ intent,
+ fromUid = 10000,
+ fromPackage = "com.android.example",
referrer = "android-app://com.android.example".toUri()
)
@Test
fun missingIntent() {
- val args = bundleOf()
-
- val result = readChooserRequest(callerInfo, args::get)
+ val result = readChooserRequest(mActivityLaunch)
assertThat(result).value().isNull()
assertThat(result)
@@ -51,13 +52,13 @@ class ChooserRequestTest {
@Test
fun minimal() {
- val args = bundleOf(EXTRA_INTENT to Intent(ACTION_SEND))
+ intent.putExtras(bundleOf(EXTRA_INTENT to Intent(ACTION_SEND)))
- val result = readChooserRequest(callerInfo, args::get)
+ val result = readChooserRequest(mActivityLaunch)
assertThat(result).value().isNotNull()
val value: ChooserRequest = result.getOrThrow()
- assertThat(value.launchedFromPackage).isEqualTo(callerInfo.launchedFomPackage)
+ assertThat(value.launchedFromPackage).isEqualTo(mActivityLaunch.fromPackage)
assertThat(result).findings().isEmpty()
}
}