Return app hidden details activity in launcher api

If a normal app does not have launcher icon, launcher api
will return app details activity instead, so user will
be noticed that the app is still installed.

Bug: 111348460

Test: Installed an app without launcher activity, an app icon is being
shown in launcher allapps, and it forwards user to app details page.

Change-Id: I9c17f5edfdefe19727145e7176d7e113286c997d
This commit is contained in:
Ricky Wai
2018-10-10 09:26:32 +01:00
parent bfec1e9843
commit cf134ebfb7
5 changed files with 233 additions and 12 deletions

View File

@@ -0,0 +1,36 @@
/*
* Copyright 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.app;
import android.content.Intent;
import android.os.Bundle;
/**
* Helper activity that forwards you to app details page.
*
* @hide
*/
public class AppDetailsActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(android.net.Uri.fromParts("package", getPackageName(), null));
startActivity(intent);
finish();
}
}

View File

@@ -19,6 +19,7 @@ package android.content.pm;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager.ApplicationInfoFlags;
@@ -204,6 +205,29 @@ public abstract class PackageManagerInternal {
public abstract PackageInfo getPackageInfo(String packageName,
@PackageInfoFlags int flags, int filterCallingUid, int userId);
/**
* Return a List of all application packages that are installed on the
* device, for a specific user. If flag GET_UNINSTALLED_PACKAGES has been
* set, a list of all applications including those deleted with
* {@code DONT_DELETE_DATA} (partially installed apps with data directory)
* will be returned.
*
* @param flags Additional option flags to modify the data returned.
* @param userId The user for whom the installed applications are to be
* listed
* @param callingUid The uid of the original caller app
* @return A List of ApplicationInfo objects, one for each installed
* application. In the unlikely case there are no installed
* packages, an empty list is returned. If flag
* {@code MATCH_UNINSTALLED_PACKAGES} is set, the application
* information is retrieved from the list of uninstalled
* applications (which includes installed applications as well as
* applications with data directory i.e. applications which had been
* deleted with {@code DONT_DELETE_DATA} flag set).
*/
public abstract List<ApplicationInfo> getInstalledApplications(
@ApplicationInfoFlags int flags, @UserIdInt int userId, int callingUid);
/**
* Retrieve launcher extras for a suspended package provided to the system in
* {@link PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,

View File

@@ -47,6 +47,7 @@ import android.annotation.Nullable;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityTaskManager;
import android.app.AppDetailsActivity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
@@ -3876,6 +3877,11 @@ public class PackageParser {
}
}
// Add a hidden app detail activity which forwards user to App Details page.
Activity a = generateAppDetailsHiddenActivity(owner, flags, outError,
owner.baseHardwareAccelerated);
owner.activities.add(a);
if (hasActivityOrder) {
Collections.sort(owner.activities, (a1, a2) -> Integer.compare(a2.order, a1.order));
}
@@ -4115,9 +4121,14 @@ public class PackageParser {
return false;
}
} else {
outInfo.name
String outInfoName
= buildClassName(owner.applicationInfo.packageName, name, outError);
if (outInfo.name == null) {
if (AppDetailsActivity.class.getName().equals(outInfoName)) {
outError[0] = tag + " invalid android:name";
return false;
}
outInfo.name = outInfoName;
if (outInfoName == null) {
return false;
}
}
@@ -4156,6 +4167,45 @@ public class PackageParser {
return true;
}
/**
* Generate activity object that forwards user to App Details page automatically.
* This activity should be invisible to user and user should not know or see it.
*/
private @NonNull PackageParser.Activity generateAppDetailsHiddenActivity(
PackageParser.Package owner, int flags, String[] outError,
boolean hardwareAccelerated) {
// Build custom App Details activity info instead of parsing it from xml
Activity a = new Activity(owner, AppDetailsActivity.class.getName(), new ActivityInfo());
a.owner = owner;
a.setPackageName(owner.packageName);
a.info.theme = 0;
a.info.exported = true;
a.info.name = AppDetailsActivity.class.getName();
a.info.processName = owner.applicationInfo.processName;
a.info.uiOptions = a.info.applicationInfo.uiOptions;
a.info.taskAffinity = buildTaskAffinityName(owner.packageName, owner.packageName,
":app_details", outError);
a.info.enabled = true;
a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
a.info.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NONE;
a.info.maxRecents = ActivityTaskManager.getDefaultAppRecentsLimitStatic();
a.info.configChanges = getActivityConfigChanges(0, 0);
a.info.softInputMode = 0;
a.info.persistableMode = ActivityInfo.PERSIST_NEVER;
a.info.screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
a.info.resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
a.info.lockTaskLaunchMode = 0;
a.info.encryptionAware = a.info.directBootAware = false;
a.info.rotationAnimation = ROTATION_ANIMATION_UNSPECIFIED;
a.info.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
if (hardwareAccelerated) {
a.info.flags |= ActivityInfo.FLAG_HARDWARE_ACCELERATED;
}
return a;
}
private Activity parseActivity(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError, CachedComponentArgs cachedArgs,
boolean receiver, boolean hardwareAccelerated)
@@ -7178,10 +7228,16 @@ public class PackageParser {
ComponentName componentName;
String componentShortName;
public Component(Package _owner) {
owner = _owner;
intents = null;
className = null;
public Component(Package owner, ArrayList<II> intents, String className) {
this.owner = owner;
this.intents = intents;
this.className = className;
}
public Component(Package owner) {
this.owner = owner;
this.intents = null;
this.className = null;
}
public Component(final ParsePackageItemArgs args, final PackageItemInfo outInfo) {
@@ -7646,6 +7702,13 @@ public class PackageParser {
return mHasMaxAspectRatio;
}
// To construct custom activity which does not exist in manifest
Activity(final Package owner, final String className, final ActivityInfo info) {
super(owner, new ArrayList<>(0), className);
this.info = info;
this.info.applicationInfo = owner.applicationInfo;
}
public Activity(final ParseComponentArgs args, final ActivityInfo _info) {
super(args, _info);
info = _info;