1/*
2 * Copyright (C) 2015 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
17package com.android.tv.settings.device.apps;
18
19import android.app.ActivityManager;
20import android.content.Context;
21import android.content.pm.PackageInfo;
22import android.content.pm.PackageManager;
23import android.content.pm.ResolveInfo;
24import android.os.Bundle;
25import android.support.annotation.NonNull;
26import android.support.v17.leanback.widget.GuidanceStylist;
27
28import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
29import com.android.settingslib.Utils;
30import com.android.settingslib.applications.ApplicationsState;
31import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
32import com.android.tv.settings.R;
33
34import java.util.ArrayList;
35import java.util.HashSet;
36import java.util.List;
37
38public class EnableDisablePreference extends AppActionPreference {
39
40    private final PackageManager mPackageManager;
41
42    public EnableDisablePreference(Context context,
43            ApplicationsState.AppEntry entry) {
44        super(context, entry);
45        mPackageManager = context.getPackageManager();
46        refresh();
47    }
48
49    public void refresh() {
50        if (!UninstallPreference.canUninstall(mEntry) && canDisable()) {
51            setVisible(true);
52            if (mEntry.info.enabled) {
53                setTitle(R.string.device_apps_app_management_disable);
54                ConfirmationFragment.prepareArgs(getExtras(), mEntry.info.packageName, false);
55            } else {
56                setTitle(R.string.device_apps_app_management_enable);
57                ConfirmationFragment.prepareArgs(getExtras(), mEntry.info.packageName, true);
58            }
59        } else {
60            setVisible(false);
61        }
62    }
63
64    private boolean canDisable() {
65        final HashSet<String> homePackages = getHomePackages();
66        PackageInfo packageInfo;
67        try {
68            packageInfo = mPackageManager.getPackageInfo(mEntry.info.packageName,
69                    PackageManager.GET_DISABLED_COMPONENTS |
70                            PackageManager.GET_UNINSTALLED_PACKAGES |
71                            PackageManager.GET_SIGNATURES);
72        } catch (PackageManager.NameNotFoundException e) {
73            return false;
74        }
75        return !(homePackages.contains(mEntry.info.packageName) ||
76                Utils.isSystemPackage(getContext().getResources(), mPackageManager, packageInfo));
77    }
78
79    private HashSet<String> getHomePackages() {
80        HashSet<String> homePackages = new HashSet<>();
81        // Get list of "home" apps and trace through any meta-data references
82        List<ResolveInfo> homeActivities = new ArrayList<>();
83        mPackageManager.getHomeActivities(homeActivities);
84        for (ResolveInfo ri : homeActivities) {
85            final String activityPkg = ri.activityInfo.packageName;
86            homePackages.add(activityPkg);
87            // Also make sure to include anything proxying for the home app
88            final Bundle metadata = ri.activityInfo.metaData;
89            if (metadata != null) {
90                final String metaPkg = metadata.getString(ActivityManager.META_HOME_ALTERNATE);
91                if (signaturesMatch(mPackageManager, metaPkg, activityPkg)) {
92                    homePackages.add(metaPkg);
93                }
94            }
95        }
96        return homePackages;
97    }
98
99    private static boolean signaturesMatch(PackageManager pm, String pkg1, String pkg2) {
100        if (pkg1 != null && pkg2 != null) {
101            try {
102                final int match = pm.checkSignatures(pkg1, pkg2);
103                if (match >= PackageManager.SIGNATURE_MATCH) {
104                    return true;
105                }
106            } catch (Exception e) {
107                // e.g. named alternate package not found during lookup;
108                // this is an expected case sometimes
109            }
110        }
111        return false;
112    }
113
114    @Override
115    public String getFragment() {
116        return ConfirmationFragment.class.getName();
117    }
118
119    public static class ConfirmationFragment extends AppActionPreference.ConfirmationFragment {
120        private static final String ARG_PACKAGE_NAME = "packageName";
121        private static final String ARG_ENABLE = "enable";
122
123        private static void prepareArgs(@NonNull Bundle args, String packageName, boolean enable) {
124            args.putString(ARG_PACKAGE_NAME, packageName);
125            args.putBoolean(ARG_ENABLE, enable);
126        }
127
128        private final MetricsFeatureProvider mMetricsFeatureProvider = new MetricsFeatureProvider();
129
130        @NonNull
131        @Override
132        public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
133            final AppManagementFragment fragment = (AppManagementFragment) getTargetFragment();
134            final Boolean enable = getArguments().getBoolean(ARG_ENABLE);
135            return new GuidanceStylist.Guidance(
136                    getString(enable ? R.string.device_apps_app_management_enable :
137                            R.string.device_apps_app_management_disable),
138                    getString(enable ? R.string.device_apps_app_management_enable_desc :
139                            R.string.device_apps_app_management_disable_desc),
140                    fragment.getAppName(),
141                    fragment.getAppIcon());
142        }
143
144        @Override
145        public void onOk() {
146            boolean enable = getArguments().getBoolean(ARG_ENABLE);
147            mMetricsFeatureProvider.action(getContext(), enable
148                    ? MetricsEvent.ACTION_SETTINGS_ENABLE_APP
149                    : MetricsEvent.ACTION_SETTINGS_DISABLE_APP);
150            getActivity().getPackageManager().setApplicationEnabledSetting(
151                    getArguments().getString(ARG_PACKAGE_NAME), enable
152                            ? PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
153                            : PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER,
154                    0);
155        }
156    }
157}
158