• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 android.ondevicepersonalization;
18 
19 import android.annotation.CallbackExecutor;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.ServiceConnection;
26 import android.content.pm.ResolveInfo;
27 import android.content.pm.ServiceInfo;
28 import android.ondevicepersonalization.aidl.IPrivacyStatusService;
29 import android.ondevicepersonalization.aidl.IPrivacyStatusServiceCallback;
30 import android.os.IBinder;
31 import android.os.OutcomeReceiver;
32 import android.os.RemoteException;
33 import android.util.Slog;
34 
35 import java.util.List;
36 import java.util.concurrent.CountDownLatch;
37 import java.util.concurrent.Executor;
38 import java.util.concurrent.TimeUnit;
39 
40 /**
41  * OnDevicePersonalizationPrivacyStatusManager provides system APIs
42  * for GMSCore to control privacy statuses of ODP users.
43  * @hide
44  */
45 public class OnDevicePersonalizationPrivacyStatusManager {
46     public static final String ON_DEVICE_PERSONALIZATION_PRIVACY_STATUS_SERVICE =
47             "on_device_personalization_privacy_status_service";
48     private static final String TAG = "OdpPrivacyStatusManager";
49     private static final String ODP_PRIVACY_STATUS_SERVICE_INTENT =
50             "android.OnDevicePersonalizationPrivacyStatusService";
51     private boolean mBound = false;
52     private IPrivacyStatusService mService = null;
53     private final Context mContext;
54     private final CountDownLatch mConnectionLatch = new CountDownLatch(1);
55 
OnDevicePersonalizationPrivacyStatusManager(@onNull Context context)56     public OnDevicePersonalizationPrivacyStatusManager(@NonNull Context context) {
57         mContext = context;
58     }
59 
60     private final ServiceConnection mConnection = new ServiceConnection() {
61         @Override
62         public void onServiceConnected(ComponentName name, IBinder binder) {
63             mService = IPrivacyStatusService.Stub.asInterface(binder);
64             mBound = true;
65             mConnectionLatch.countDown();
66         }
67 
68         @Override
69         public void onNullBinding(ComponentName name) {
70             mBound = false;
71             mConnectionLatch.countDown();
72         }
73 
74         @Override
75         public void onServiceDisconnected(ComponentName name) {
76             mService = null;
77             mBound = false;
78         }
79     };
80 
81     private static final int BIND_SERVICE_TIMEOUT_SEC = 5;
82 
83     /**
84      * Modify the user's kid status in ODP from GMSCore.
85      *
86      * @param isKidStatusEnabled user's kid status available at GMSCore.
87      * @param executor the {@link Executor} on which to invoke the callback
88      * @param receiver This either returns true on success or {@link Exception} on failure.
89      * @hide
90      */
setKidStatus(boolean isKidStatusEnabled, @NonNull @CallbackExecutor Executor executor, @NonNull OutcomeReceiver<Boolean, Exception> receiver)91     public void setKidStatus(boolean isKidStatusEnabled,
92             @NonNull @CallbackExecutor Executor executor,
93             @NonNull OutcomeReceiver<Boolean, Exception> receiver) {
94         try {
95             bindService(executor);
96 
97             mService.setKidStatus(isKidStatusEnabled, new IPrivacyStatusServiceCallback.Stub() {
98                 @Override
99                 public void onSuccess() {
100                     executor.execute(() -> receiver.onResult(true));
101                 }
102 
103                 @Override
104                 public void onFailure(int errorCode) {
105                     executor.execute(() -> receiver.onError(
106                             new OnDevicePersonalizationException(errorCode)));
107                 }
108             });
109         } catch (InterruptedException | RemoteException e) {
110             receiver.onError(e);
111         }
112     }
113 
bindService(@onNull Executor executor)114     private void bindService(@NonNull Executor executor) throws InterruptedException {
115         if (!mBound) {
116             Intent intent = new Intent(ODP_PRIVACY_STATUS_SERVICE_INTENT);
117             ComponentName serviceComponent = resolveService(intent);
118             if (serviceComponent == null) {
119                 Slog.e(TAG, "Invalid component for ODP privacy status service");
120                 return;
121             }
122 
123             intent.setComponent(serviceComponent);
124             boolean r = mContext.bindService(
125                     intent, Context.BIND_AUTO_CREATE, executor, mConnection);
126             if (!r) {
127                 return;
128             }
129             mConnectionLatch.await(BIND_SERVICE_TIMEOUT_SEC, TimeUnit.SECONDS);
130         }
131     }
132 
133     /**
134      * Find the ComponentName of the service, given its intent.
135      *
136      * @return ComponentName of the service. Null if the service is not found.
137      */
138     @Nullable
resolveService(@onNull Intent intent)139     private ComponentName resolveService(@NonNull Intent intent) {
140         List<ResolveInfo> services = mContext.getPackageManager().queryIntentServices(intent, 0);
141         if (services == null || services.isEmpty()) {
142             Slog.e(TAG, "Failed to find OdpPrivacyStatus service");
143             return null;
144         }
145 
146         for (int i = 0; i < services.size(); i++) {
147             ServiceInfo serviceInfo = services.get(i).serviceInfo;
148             if (serviceInfo == null) {
149                 Slog.e(TAG, "Failed to find serviceInfo for OdpPrivacyStatus service.");
150                 return null;
151             }
152             // There should only be one matching service inside the given package.
153             // If there's more than one, return the first one found.
154             return new ComponentName(serviceInfo.packageName, serviceInfo.name);
155         }
156         Slog.e(TAG, "Didn't find any matching OdpPrivacyStatus service.");
157         return null;
158     }
159 }
160