1 /*
2  * Copyright (C) 2023 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 package com.android.server.ondevicepersonalization;
17 
18 import static android.adservices.ondevicepersonalization.OnDevicePersonalizationPermissions.ACCESS_SYSTEM_SERVER_SERVICE;
19 import static android.ondevicepersonalization.OnDevicePersonalizationSystemServiceManager.ON_DEVICE_PERSONALIZATION_SYSTEM_SERVICE;
20 
21 import android.adservices.ondevicepersonalization.Constants;
22 import android.content.Context;
23 import android.content.pm.PackageManager;
24 import android.ondevicepersonalization.IOnDevicePersonalizationSystemService;
25 import android.ondevicepersonalization.IOnDevicePersonalizationSystemServiceCallback;
26 import android.os.Bundle;
27 import android.os.RemoteException;
28 import android.util.Log;
29 
30 import com.android.internal.annotations.VisibleForTesting;
31 import com.android.server.SystemService;
32 
33 import java.util.Objects;
34 
35 /**
36  * @hide
37  */
38 public class OnDevicePersonalizationSystemService
39         extends IOnDevicePersonalizationSystemService.Stub {
40     private static final String TAG = "ondevicepersonalization";
41     // TODO(b/302991763): set up per-user directory if needed.
42     private static final String ODP_BASE_DIR = "/data/system/ondevicepersonalization/0/";
43     private static final String CONFIG_FILE_IDENTIFIER = "CONFIG";
44     public static final String PERSONALIZATION_STATUS_KEY = "PERSONALIZATION_STATUS";
45     private final Context mContext;
46     private BooleanFileDataStore mDataStore = null;
47 
48     // TODO(b/302992251): use a manager to access configs instead of directly exposing DataStore.
49 
OnDevicePersonalizationSystemService(Context context)50     OnDevicePersonalizationSystemService(Context context) {
51         this(context, new BooleanFileDataStore(ODP_BASE_DIR, CONFIG_FILE_IDENTIFIER));
52     }
53 
54     @VisibleForTesting
OnDevicePersonalizationSystemService(Context context, BooleanFileDataStore dataStore)55     OnDevicePersonalizationSystemService(Context context, BooleanFileDataStore dataStore) {
56         Objects.requireNonNull(context);
57         Objects.requireNonNull(dataStore);
58         mContext = context;
59         try {
60             this.mDataStore = dataStore;
61             mDataStore.initialize();
62         } catch (Exception e) {
63             Log.e(TAG, "Cannot initialize system service datastore.", e);
64             mDataStore = null;
65         }
66     }
67 
onRequest( Bundle bundle, IOnDevicePersonalizationSystemServiceCallback callback)68     @Override public void onRequest(
69             Bundle bundle,
70             IOnDevicePersonalizationSystemServiceCallback callback) {
71         enforceCallingPermission();
72         sendResult(callback, null);
73     }
74 
75     @Override
setPersonalizationStatus( boolean enabled, IOnDevicePersonalizationSystemServiceCallback callback)76     public void setPersonalizationStatus(
77             boolean enabled,
78             IOnDevicePersonalizationSystemServiceCallback callback) {
79         enforceCallingPermission();
80         Bundle result = new Bundle();
81         try {
82             mDataStore.put(PERSONALIZATION_STATUS_KEY, enabled);
83             // Confirm the value was updated.
84             Boolean statusResult = mDataStore.get(PERSONALIZATION_STATUS_KEY);
85             if (statusResult == null || statusResult.booleanValue() != enabled) {
86                 sendError(callback, Constants.STATUS_INTERNAL_ERROR);
87                 return;
88             }
89             // Echo the result back
90             result.putBoolean(PERSONALIZATION_STATUS_KEY, statusResult);
91         } catch (Exception e) {
92             Log.e(TAG, "Unable to persist personalization status", e);
93             sendError(callback, Constants.STATUS_INTERNAL_ERROR);
94             return;
95         }
96 
97         sendResult(callback, result);
98     }
99 
100     @Override
readPersonalizationStatus( IOnDevicePersonalizationSystemServiceCallback callback)101     public void readPersonalizationStatus(
102             IOnDevicePersonalizationSystemServiceCallback callback) {
103         enforceCallingPermission();
104         Boolean result = null;
105 
106         try {
107             result = mDataStore.get(PERSONALIZATION_STATUS_KEY);
108         } catch (Exception e) {
109             Log.e(TAG, "Error reading datastore", e);
110             sendError(callback, Constants.STATUS_INTERNAL_ERROR);
111             return;
112         }
113 
114         if (result == null) {
115             Log.d(TAG, "Unable to restore personalization status");
116             sendError(callback, Constants.STATUS_KEY_NOT_FOUND);
117         } else {
118             Bundle bundle = new Bundle();
119             bundle.putBoolean(PERSONALIZATION_STATUS_KEY, result.booleanValue());
120             sendResult(callback, bundle);
121         }
122     }
123 
sendResult( IOnDevicePersonalizationSystemServiceCallback callback, Bundle bundle)124     private void sendResult(
125             IOnDevicePersonalizationSystemServiceCallback callback, Bundle bundle) {
126         try {
127             callback.onResult(bundle);
128         } catch (RemoteException e) {
129             Log.e(TAG, "Callback error", e);
130         }
131     }
132 
sendError( IOnDevicePersonalizationSystemServiceCallback callback, int errorCode)133     private void sendError(
134             IOnDevicePersonalizationSystemServiceCallback callback, int errorCode) {
135         try {
136             callback.onError(errorCode);
137         } catch (RemoteException e) {
138             Log.e(TAG, "Callback error", e);
139         }
140     }
141 
142     @VisibleForTesting
enforceCallingPermission()143     void enforceCallingPermission() {
144         if (mContext.checkCallingPermission(ACCESS_SYSTEM_SERVER_SERVICE)
145                 != PackageManager.PERMISSION_GRANTED) {
146             throw new SecurityException("ODP System Service Permission denied");
147         }
148     }
149 
150     /** @hide */
151     public static class Lifecycle extends SystemService {
152         private OnDevicePersonalizationSystemService mService;
153 
154         /** @hide */
Lifecycle(Context context)155         public Lifecycle(Context context) {
156             super(context);
157             if (!isOdpSupported(context)) {
158                 return;
159             }
160             mService = new OnDevicePersonalizationSystemService(getContext());
161         }
162 
163         /** @hide */
164         @Override
onStart()165         public void onStart() {
166             if (mService == null || mService.mDataStore == null) {
167                 Log.e(TAG, "OnDevicePersonalizationSystemService not started!");
168                 return;
169             }
170             publishBinderService(ON_DEVICE_PERSONALIZATION_SYSTEM_SERVICE, mService);
171             Log.i(TAG, "OnDevicePersonalizationSystemService started!");
172         }
173 
174             /** Returns true if the device supports ODP. */
isOdpSupported(Context context)175         private static boolean isOdpSupported(Context context) {
176             final PackageManager pm = context.getPackageManager();
177             if (pm == null) {
178                 Log.e(TAG, "PackageManager not found.");
179                 return true;
180             }
181             return !pm.hasSystemFeature(PackageManager.FEATURE_WATCH)
182                     && !pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
183                     // Android TV
184                     && !pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
185                     // Android Go
186                     && !pm.hasSystemFeature(PackageManager.FEATURE_RAM_LOW);
187         }
188     }
189 }
190