1 /* 2 * Copyright (C) 2020 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.car.admin; 17 18 import static com.android.car.admin.CarDevicePolicyService.DEBUG; 19 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 20 21 import android.annotation.IntDef; 22 import android.app.admin.DevicePolicyManager; 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.util.DebugUtils; 28 import android.util.IndentingPrintWriter; 29 import android.util.Slog; 30 31 import com.android.car.CarLog; 32 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 33 import com.android.internal.annotations.GuardedBy; 34 import com.android.internal.annotations.VisibleForTesting; 35 36 import java.lang.annotation.Retention; 37 import java.lang.annotation.RetentionPolicy; 38 import java.util.Objects; 39 40 /** 41 * User-specific {@code CarDevicePolicyManagerService}. 42 */ 43 public final class PerUserCarDevicePolicyService { 44 45 private static final String TAG = CarLog.tagFor(PerUserCarDevicePolicyService.class); 46 47 private static final String PREFIX_NEW_USER_DISCLAIMER_STATUS = "NEW_USER_DISCLAIMER_STATUS_"; 48 49 // TODO(b/175057848) must be public because of DebugUtils.constantToString() 50 public static final int NEW_USER_DISCLAIMER_STATUS_NEVER_RECEIVED = 0; 51 public static final int NEW_USER_DISCLAIMER_STATUS_RECEIVED = 1; 52 public static final int NEW_USER_DISCLAIMER_STATUS_NOTIFICATION_SENT = 2; 53 public static final int NEW_USER_DISCLAIMER_STATUS_SHOWN = 3; 54 public static final int NEW_USER_DISCLAIMER_STATUS_ACKED = 4; 55 56 @Retention(RetentionPolicy.SOURCE) 57 @IntDef(flag = false, prefix = { PREFIX_NEW_USER_DISCLAIMER_STATUS }, value = { 58 NEW_USER_DISCLAIMER_STATUS_NEVER_RECEIVED, 59 NEW_USER_DISCLAIMER_STATUS_NOTIFICATION_SENT, 60 NEW_USER_DISCLAIMER_STATUS_RECEIVED, 61 NEW_USER_DISCLAIMER_STATUS_SHOWN, 62 NEW_USER_DISCLAIMER_STATUS_ACKED 63 }) 64 public @interface NewUserDisclaimerStatus {} 65 66 private static final Object SLOCK = new Object(); 67 68 @GuardedBy("SLOCK") 69 private static PerUserCarDevicePolicyService sInstance; 70 71 private final Context mContext; 72 73 @GuardedBy("sLock") 74 @NewUserDisclaimerStatus 75 private int mNewUserDisclaimerStatus = NEW_USER_DISCLAIMER_STATUS_NEVER_RECEIVED; 76 77 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 78 @Override 79 public void onReceive(Context context, Intent intent) { 80 if (DEBUG) { 81 Slog.d(TAG, "Received intent on user " + mContext.getUserId() + ": " + intent); 82 } 83 switch(intent.getAction()) { 84 case DevicePolicyManager.ACTION_SHOW_NEW_USER_DISCLAIMER: 85 setUserDisclaimerStatus(NEW_USER_DISCLAIMER_STATUS_RECEIVED); 86 showNewUserDisclaimer(); 87 break; 88 default: 89 Slog.w(TAG, "received unexpected intent: " + intent); 90 } 91 } 92 }; 93 94 /** 95 * Gests the singleton instance, creating it if necessary. 96 */ getInstance(Context context)97 public static PerUserCarDevicePolicyService getInstance(Context context) { 98 Objects.requireNonNull(context, "context cannot be null"); 99 100 synchronized (SLOCK) { 101 if (sInstance == null) { 102 sInstance = new PerUserCarDevicePolicyService(context.getApplicationContext()); 103 if (DEBUG) Slog.d(TAG, "Created instance: " + sInstance); 104 } 105 106 return sInstance; 107 } 108 } 109 110 @VisibleForTesting PerUserCarDevicePolicyService(Context context)111 PerUserCarDevicePolicyService(Context context) { 112 mContext = context; 113 } 114 115 /** 116 * Callback for when the service is created. 117 */ onCreate()118 public void onCreate() { 119 if (DEBUG) Slog.d(TAG, "registering BroadcastReceiver"); 120 121 mContext.registerReceiver(mBroadcastReceiver, new IntentFilter( 122 DevicePolicyManager.ACTION_SHOW_NEW_USER_DISCLAIMER)); 123 } 124 125 /** 126 * Callback for when the service is not needed anymore. 127 */ onDestroy()128 public void onDestroy() { 129 synchronized (SLOCK) { 130 sInstance = null; 131 } 132 if (DEBUG) Slog.d(TAG, "unregistering BroadcastReceiver"); 133 mContext.unregisterReceiver(mBroadcastReceiver); 134 } 135 136 /** 137 * Dump its contents. 138 */ 139 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter pw)140 public void dump(IndentingPrintWriter pw) { 141 synchronized (SLOCK) { 142 pw.printf("mNewUserDisclaimerStatus: %s\n", 143 newUserDisclaimerStatusToString(mNewUserDisclaimerStatus)); 144 } 145 } 146 setShown()147 void setShown() { 148 setUserDisclaimerStatus(NEW_USER_DISCLAIMER_STATUS_SHOWN); 149 } 150 setAcknowledged()151 void setAcknowledged() { 152 setUserDisclaimerStatus(NEW_USER_DISCLAIMER_STATUS_ACKED); 153 NewUserDisclaimerActivity.cancelNotification(mContext); 154 155 DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); 156 dpm.resetNewUserDisclaimer(); 157 } 158 showNewUserDisclaimer()159 private void showNewUserDisclaimer() { 160 // TODO(b/175057848) persist status so it's shown again if car service crashes? 161 NewUserDisclaimerActivity.showNotification(mContext); 162 setUserDisclaimerStatus(NEW_USER_DISCLAIMER_STATUS_NOTIFICATION_SENT); 163 } 164 setUserDisclaimerStatus(@ewUserDisclaimerStatus int status)165 private void setUserDisclaimerStatus(@NewUserDisclaimerStatus int status) { 166 synchronized (SLOCK) { 167 if (DEBUG) { 168 Slog.d(TAG, "Changinging status from " 169 + newUserDisclaimerStatusToString(mNewUserDisclaimerStatus) + " to " 170 + newUserDisclaimerStatusToString(status)); 171 } 172 mNewUserDisclaimerStatus = status; 173 } 174 } 175 176 @VisibleForTesting 177 @NewUserDisclaimerStatus getNewUserDisclaimerStatus()178 int getNewUserDisclaimerStatus() { 179 synchronized (SLOCK) { 180 return mNewUserDisclaimerStatus; 181 } 182 } 183 184 @VisibleForTesting newUserDisclaimerStatusToString(@ewUserDisclaimerStatus int status)185 static String newUserDisclaimerStatusToString(@NewUserDisclaimerStatus int status) { 186 return DebugUtils.constantToString(PerUserCarDevicePolicyService.class, 187 PREFIX_NEW_USER_DISCLAIMER_STATUS, status); 188 } 189 } 190