1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.applications;
18 
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.pm.ApplicationInfo;
22 import android.content.pm.InstallSourceInfo;
23 import android.content.pm.PackageManager.NameNotFoundException;
24 import android.content.pm.ResolveInfo;
25 import android.text.TextUtils;
26 import android.util.Log;
27 import android.util.Pair;
28 
29 import androidx.annotation.NonNull;
30 import androidx.annotation.Nullable;
31 
32 import java.util.Objects;
33 
34 /** This class provides methods that help dealing with app stores. */
35 public class AppStoreUtil {
36     private static final String LOG_TAG = "AppStoreUtil";
37 
resolveIntent(Context context, Intent i)38     private static Intent resolveIntent(Context context, Intent i) {
39         ResolveInfo result = context.getPackageManager().resolveActivity(i, 0);
40         return result != null ? new Intent(i.getAction())
41                 .setClassName(result.activityInfo.packageName, result.activityInfo.name) : null;
42     }
43 
44     /**
45      * Returns a {@link Pair pair result}. The first item is the package name of the app that we
46      * consider to be the user-visible 'installer' of given packageName, if one is available. The
47      * second item is the {@link InstallSourceInfo install source info} of the given package.
48      */
49     @NonNull
getInstallerPackageNameAndInstallSourceInfo( @onNull Context context, @NonNull String packageName)50     public static Pair<String, InstallSourceInfo> getInstallerPackageNameAndInstallSourceInfo(
51             @NonNull Context context, @NonNull String packageName) {
52         Objects.requireNonNull(context);
53         Objects.requireNonNull(packageName);
54 
55         String installerPackageName;
56         InstallSourceInfo source = null;
57         try {
58             source = context.getPackageManager().getInstallSourceInfo(packageName);
59             // By default, use the installing package name.
60             installerPackageName = source.getInstallingPackageName();
61             // Use the recorded originating package name only if the initiating package is a system
62             // app (eg. Package Installer). The originating package is not verified by the platform,
63             // so we choose to ignore this when supplied by a non-system app.
64             String originatingPackageName = source.getOriginatingPackageName();
65             String initiatingPackageName = source.getInitiatingPackageName();
66             if (originatingPackageName != null && initiatingPackageName != null
67                     && !initiatingPackageName.equals("com.android.shell")) {
68                 ApplicationInfo ai = context.getPackageManager().getApplicationInfo(
69                         initiatingPackageName, 0);
70                 if ((ai.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
71                     installerPackageName = originatingPackageName;
72                 }
73             }
74         } catch (NameNotFoundException e) {
75             Log.e(LOG_TAG, "Exception while retrieving the package installer of " + packageName, e);
76             installerPackageName = null;
77         }
78         return new Pair<>(installerPackageName, source);
79     }
80 
81     /**
82      * Returns the package name of the app that we consider to be the user-visible 'installer'
83      * of given packageName, if one is available.
84      */
85     @Nullable
getInstallerPackageName(@onNull Context context, @NonNull String packageName)86     public static String getInstallerPackageName(@NonNull Context context,
87             @NonNull String packageName) {
88         return getInstallerPackageNameAndInstallSourceInfo(context, packageName).first;
89     }
90 
91     /** Returns a link to the installer app store for a given package name. */
92     @Nullable
getAppStoreLink(Context context, String installerPackageName, String packageName)93     public static Intent getAppStoreLink(Context context, String installerPackageName,
94             String packageName) {
95         Intent intent = new Intent(Intent.ACTION_SHOW_APP_INFO)
96                 .setPackage(installerPackageName);
97         final Intent result = resolveIntent(context, intent);
98         if (result != null) {
99             result.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
100             return result;
101         }
102         return null;
103     }
104 
105     /** Convenience method that looks up the installerPackageName for you. */
106     @Nullable
getAppStoreLink(Context context, String packageName)107     public static Intent getAppStoreLink(Context context, String packageName) {
108       String installerPackageName = getInstallerPackageName(context, packageName);
109       return getAppStoreLink(context, installerPackageName, packageName);
110     }
111 
112     /**
113      * Returns {@code true} when the initiating package is different from installing package
114      * for the given {@link InstallSourceInfo install source}. Otherwise, returns {@code false}.
115      * If the {@code source} is null, also return {@code false}.
116      */
isInitiatedFromDifferentPackage(@ullable InstallSourceInfo source)117     public static boolean isInitiatedFromDifferentPackage(@Nullable InstallSourceInfo source) {
118         if (source == null) {
119             return false;
120         }
121         final String initiatingPackageName = source.getInitiatingPackageName();
122         return initiatingPackageName != null
123                 && !TextUtils.equals(source.getInstallingPackageName(), initiatingPackageName);
124     }
125 }
126