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 com.android.car.provision;
18 
19 import android.app.Service;
20 import android.content.Intent;
21 import android.os.Binder;
22 import android.os.Handler;
23 import android.os.IBinder;
24 import android.os.Looper;
25 import android.os.Parcel;
26 import android.os.RemoteException;
27 import android.util.Log;
28 
29 import com.android.internal.annotations.GuardedBy;
30 
31 /**
32  * Default implementing UserNoticeUI. There will not be any UI shown but only message in logcat.
33  */
34 public final class UserNoticeUiService extends Service {
35 
36     private static final String TAG = UserNoticeUiService.class.getSimpleName();
37 
38     private static final String IUSER_NOTICE_BINDER_DESCRIPTOR = "android.car.user.IUserNotice";
39     private static final int IUSER_NOTICE_TR_ON_LOGGED =
40             android.os.IBinder.FIRST_CALL_TRANSACTION;
41 
42     private static final String IUSER_NOTICE_UI_BINDER_DESCRIPTOR =
43             "android.car.user.IUserNoticeUI";
44     private static final int IUSER_NOTICE_UI_BINDER_TR_SET_CALLBACK =
45             android.os.IBinder.FIRST_CALL_TRANSACTION;
46 
47     private final Handler mMainHandler = new Handler(Looper.getMainLooper());
48 
49     private final Object mLock = new Object();
50 
51     // Do not use IUserNoticeUI class intentionally to show how it can be
52     // implemented without accessing the hidden API.
53     private final IBinder mIUserNoticeUiBinder = new Binder() {
54         @Override
55         protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
56                 throws RemoteException {
57             switch (code) {
58                 case IUSER_NOTICE_UI_BINDER_TR_SET_CALLBACK:
59                     Log.d(TAG, "onTransact, received call to set callBack");
60                     data.enforceInterface(IUSER_NOTICE_UI_BINDER_DESCRIPTOR);
61                     IBinder binder = data.readStrongBinder();
62                     onSetCallbackBinder(binder);
63                     return true;
64                 default:
65                     return super.onTransact(code, data, reply, flags);
66             }
67         }
68     };
69 
70     @GuardedBy("mLock")
71     private IBinder mIUserNoticeService;
72 
73     @Override
onBind(Intent intent)74     public IBinder onBind(Intent intent) {
75         return mIUserNoticeUiBinder;
76     }
77 
onSetCallbackBinder(IBinder binder)78     private void onSetCallbackBinder(IBinder binder) {
79         if (binder == null) {
80             Log.wtf(TAG, "No binder set in onSetCallbackBinder call", new RuntimeException());
81             return;
82         }
83         mMainHandler.post(() -> {
84             synchronized (mLock) {
85                 mIUserNoticeService = binder;
86             }
87             showMessage();
88             stopService();
89         });
90     }
91 
showMessage()92     private void showMessage() {
93         Log.i(TAG, "showing user notice for user: " + getUserId());
94     }
95 
stopService()96     private void stopService() {
97         IBinder userNotice;
98         synchronized (mLock) {
99             userNotice = mIUserNoticeService;
100             mIUserNoticeService = null;
101         }
102         if (userNotice != null) {
103             sendOnLoggedToCarService(userNotice);
104         }
105         stopSelf();
106     }
107 
sendOnLoggedToCarService(IBinder userNotice)108     private void sendOnLoggedToCarService(IBinder userNotice) {
109         Parcel data = Parcel.obtain();
110         data.writeInterfaceToken(IUSER_NOTICE_BINDER_DESCRIPTOR);
111         try {
112             userNotice.transact(IUSER_NOTICE_TR_ON_LOGGED, data, null, 0);
113         } catch (RemoteException e) {
114             Log.w(TAG, "CarService crashed, finish now");
115             stopSelf();
116         }
117     }
118 }
119