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