1/*
2 * Copyright (C) 2014 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.server.restrictions;
18
19import android.app.AppGlobals;
20import android.app.admin.IDevicePolicyManager;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IRestrictionsManager;
25import android.content.RestrictionsManager;
26import android.content.pm.ResolveInfo;
27import android.os.Binder;
28import android.os.Bundle;
29import android.os.IUserManager;
30import android.os.PersistableBundle;
31import android.os.RemoteException;
32import android.os.UserHandle;
33import android.util.Log;
34
35import com.android.internal.util.ArrayUtils;
36import com.android.server.SystemService;
37
38/**
39 * SystemService wrapper for the RestrictionsManager implementation. Publishes the
40 * Context.RESTRICTIONS_SERVICE.
41 */
42public final class RestrictionsManagerService extends SystemService {
43
44    static final String LOG_TAG = "RestrictionsManagerService";
45    static final boolean DEBUG = false;
46
47    private final RestrictionsManagerImpl mRestrictionsManagerImpl;
48
49    public RestrictionsManagerService(Context context) {
50        super(context);
51        mRestrictionsManagerImpl = new RestrictionsManagerImpl(context);
52    }
53
54    @Override
55    public void onStart() {
56        publishBinderService(Context.RESTRICTIONS_SERVICE, mRestrictionsManagerImpl);
57    }
58
59    class RestrictionsManagerImpl extends IRestrictionsManager.Stub {
60        final Context mContext;
61        private final IUserManager mUm;
62        private final IDevicePolicyManager mDpm;
63
64        public RestrictionsManagerImpl(Context context) {
65            mContext = context;
66            mUm = (IUserManager) getBinderService(Context.USER_SERVICE);
67            mDpm = (IDevicePolicyManager) getBinderService(Context.DEVICE_POLICY_SERVICE);
68        }
69
70        @Override
71        public Bundle getApplicationRestrictions(String packageName) throws RemoteException {
72            return mUm.getApplicationRestrictions(packageName);
73        }
74
75        @Override
76        public boolean hasRestrictionsProvider() throws RemoteException {
77            int userHandle = UserHandle.getCallingUserId();
78            if (mDpm != null) {
79                long ident = Binder.clearCallingIdentity();
80                try {
81                    return mDpm.getRestrictionsProvider(userHandle) != null;
82                } finally {
83                    Binder.restoreCallingIdentity(ident);
84                }
85            } else {
86                return false;
87            }
88        }
89
90        @Override
91        public void requestPermission(final String packageName, final String requestType,
92                final String requestId,
93                final PersistableBundle requestData) throws RemoteException {
94            if (DEBUG) {
95                Log.i(LOG_TAG, "requestPermission");
96            }
97            int callingUid = Binder.getCallingUid();
98            int userHandle = UserHandle.getUserId(callingUid);
99            if (mDpm != null) {
100                long ident = Binder.clearCallingIdentity();
101                try {
102                    ComponentName restrictionsProvider =
103                            mDpm.getRestrictionsProvider(userHandle);
104                    // Check if there is a restrictions provider
105                    if (restrictionsProvider == null) {
106                        throw new IllegalStateException(
107                            "Cannot request permission without a restrictions provider registered");
108                    }
109                    // Check that the packageName matches the caller.
110                    enforceCallerMatchesPackage(callingUid, packageName, "Package name does not" +
111                            " match caller ");
112                    // Prepare and broadcast the intent to the provider
113                    Intent intent = new Intent(RestrictionsManager.ACTION_REQUEST_PERMISSION);
114                    intent.setComponent(restrictionsProvider);
115                    intent.putExtra(RestrictionsManager.EXTRA_PACKAGE_NAME, packageName);
116                    intent.putExtra(RestrictionsManager.EXTRA_REQUEST_TYPE, requestType);
117                    intent.putExtra(RestrictionsManager.EXTRA_REQUEST_ID, requestId);
118                    intent.putExtra(RestrictionsManager.EXTRA_REQUEST_BUNDLE, requestData);
119                    mContext.sendBroadcastAsUser(intent, new UserHandle(userHandle));
120                } finally {
121                    Binder.restoreCallingIdentity(ident);
122                }
123            }
124        }
125
126        @Override
127        public Intent createLocalApprovalIntent() throws RemoteException {
128            if (DEBUG) {
129                Log.i(LOG_TAG, "requestPermission");
130            }
131            final int userHandle = UserHandle.getCallingUserId();
132            if (mDpm != null) {
133                long ident = Binder.clearCallingIdentity();
134                try {
135                    ComponentName restrictionsProvider =
136                            mDpm.getRestrictionsProvider(userHandle);
137                    // Check if there is a restrictions provider
138                    if (restrictionsProvider == null) {
139                        throw new IllegalStateException(
140                            "Cannot request permission without a restrictions provider registered");
141                    }
142                    String providerPackageName = restrictionsProvider.getPackageName();
143                    Intent intent = new Intent(RestrictionsManager.ACTION_REQUEST_LOCAL_APPROVAL);
144                    intent.setPackage(providerPackageName);
145                    ResolveInfo ri = AppGlobals.getPackageManager().resolveIntent(intent,
146                            null /* resolvedType */, 0 /* flags */, userHandle);
147                    if (ri != null && ri.activityInfo != null && ri.activityInfo.exported) {
148                        intent.setComponent(new ComponentName(ri.activityInfo.packageName,
149                                ri.activityInfo.name));
150                        return intent;
151                    }
152                } finally {
153                    Binder.restoreCallingIdentity(ident);
154                }
155            }
156            return null;
157        }
158
159        @Override
160        public void notifyPermissionResponse(String packageName, PersistableBundle response)
161                throws RemoteException {
162            // Check caller
163            int callingUid = Binder.getCallingUid();
164            int userHandle = UserHandle.getUserId(callingUid);
165            if (mDpm != null) {
166                long ident = Binder.clearCallingIdentity();
167                try {
168                    ComponentName permProvider = mDpm.getRestrictionsProvider(userHandle);
169                    if (permProvider == null) {
170                        throw new SecurityException("No restrictions provider registered for user");
171                    }
172                    enforceCallerMatchesPackage(callingUid, permProvider.getPackageName(),
173                            "Restrictions provider does not match caller ");
174
175                    // Post the response to target package
176                    Intent responseIntent = new Intent(
177                            RestrictionsManager.ACTION_PERMISSION_RESPONSE_RECEIVED);
178                    responseIntent.setPackage(packageName);
179                    responseIntent.putExtra(RestrictionsManager.EXTRA_RESPONSE_BUNDLE, response);
180                    mContext.sendBroadcastAsUser(responseIntent, new UserHandle(userHandle));
181                } finally {
182                    Binder.restoreCallingIdentity(ident);
183                }
184            }
185        }
186
187        private void enforceCallerMatchesPackage(int callingUid, String packageName,
188                String message) {
189            try {
190                String[] pkgs = AppGlobals.getPackageManager().getPackagesForUid(callingUid);
191                if (pkgs != null) {
192                    if (!ArrayUtils.contains(pkgs, packageName)) {
193                        throw new SecurityException(message + callingUid);
194                    }
195                }
196            } catch (RemoteException re) {
197                // Shouldn't happen
198            }
199        }
200    }
201}
202