1 /*
2  * Copyright (C) 2019 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.experimentalcar;
18 
19 import android.annotation.MainThread;
20 import android.annotation.Nullable;
21 import android.car.IExperimentalCar;
22 import android.car.IExperimentalCarHelper;
23 import android.car.experimental.CarDriverDistractionManager;
24 import android.car.experimental.CarTestDemoExperimentalFeatureManager;
25 import android.car.experimental.ExperimentalCar;
26 import android.content.Context;
27 import android.content.pm.PackageManager;
28 import android.os.Binder;
29 import android.os.Handler;
30 import android.os.IBinder;
31 import android.os.Looper;
32 import android.os.Process;
33 import android.os.RemoteException;
34 import android.util.Log;
35 
36 import com.android.car.CarServiceBase;
37 import com.android.car.internal.util.IndentingPrintWriter;
38 import com.android.internal.annotations.GuardedBy;
39 
40 import java.io.FileDescriptor;
41 import java.io.PrintWriter;
42 import java.util.ArrayList;
43 import java.util.Arrays;
44 import java.util.List;
45 
46 /**
47  * Implements IExperimentalCar for experimental features.
48  */
49 public final class IExperimentalCarImpl extends IExperimentalCar.Stub {
50 
51     private static final String TAG = "CAR.EXPIMPL";
52 
53     private static final List<String> ALL_AVAILABLE_FEATURES = Arrays.asList(
54             ExperimentalCar.TEST_EXPERIMENTAL_FEATURE_SERVICE,
55             ExperimentalCar.DRIVER_DISTRACTION_EXPERIMENTAL_FEATURE_SERVICE
56     );
57 
58     private final Context mContext;
59 
60     private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
61 
62     private final Object mLock = new Object();
63 
64     @GuardedBy("mLock")
65     private boolean mReleased;
66 
67     @GuardedBy("mLock")
68     private ArrayList<CarServiceBase> mRunningServices = new ArrayList<>();
69 
IExperimentalCarImpl(Context context)70     public IExperimentalCarImpl(Context context) {
71         mContext = context;
72     }
73 
74     @Override
init(IExperimentalCarHelper helper, List<String> enabledFeatures)75     public void init(IExperimentalCarHelper helper, List<String> enabledFeatures) {
76         // From car service or unit testing only
77         assertCallingFromSystemProcessOrSelf();
78 
79         // dispatch to main thread as release is always done in main.
80         mMainThreadHandler.post(() -> {
81             synchronized (mLock) {
82                 if (mReleased) {
83                     Log.w(TAG, "init binder call after onDestroy, will ignore");
84                     return;
85                 }
86             }
87             ArrayList<CarServiceBase> services = new ArrayList<>();
88             ArrayList<String> startedFeatures = new ArrayList<>();
89             ArrayList<String> classNames = new ArrayList<>();
90             ArrayList<IBinder> binders = new ArrayList<>();
91 
92             // This cannot address inter-dependency. That requires re-ordering this in dependency
93             // order.
94             // That should be done when we find such needs. For now, each feature inside here should
95             // not have inter-dependency as they are all optional.
96             for (String feature : enabledFeatures) {
97                 CarServiceBase service = constructServiceForFeature(feature);
98                 if (service == null) {
99                     Log.e(TAG, "Failed to construct requested feature:" + feature);
100                     continue;
101                 }
102                 service.init();
103                 services.add(service);
104                 startedFeatures.add(feature);
105                 // If it is not IBinder, then it is internal feature.
106                 if (service instanceof IBinder) {
107                     binders.add((IBinder) service);
108                 } else {
109                     binders.add(null);
110                 }
111                 classNames.add(getClassNameForFeature(feature));
112             }
113             try {
114                 helper.onInitComplete(ALL_AVAILABLE_FEATURES, startedFeatures, classNames, binders);
115             } catch (RemoteException e) {
116                 Log.w(TAG, "Car service crashed?", e);
117                 // will be destroyed soon. Just continue and register services for possible cleanup.
118             }
119             synchronized (mLock) {
120                 mRunningServices.addAll(services);
121             }
122         });
123     }
124 
125     // should be called in Service.onDestroy
126     @MainThread
release()127     void release() {
128         // Copy to handle call release without lock
129         ArrayList<CarServiceBase> services;
130         synchronized (mLock) {
131             if (mReleased) {
132                 return;
133             }
134             mReleased = true;
135             services = new ArrayList<>(mRunningServices);
136             mRunningServices.clear();
137         }
138         for (CarServiceBase service : services) {
139             service.release();
140         }
141     }
142 
143     /** dump */
dump(FileDescriptor fd, PrintWriter writer, String[] args)144     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
145         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
146                 != PackageManager.PERMISSION_GRANTED) {
147             writer.println("Permission Denial: can't dump ExperimentalCarService from from pid="
148                     + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
149                     + " without permission " + android.Manifest.permission.DUMP);
150             return;
151         }
152 
153         try (IndentingPrintWriter pw = new IndentingPrintWriter(writer)) {
154             ArrayList<CarServiceBase> services;
155             synchronized (mLock) {
156                 pw.printf("mReleased: %b\n", mReleased);
157                 pw.printf("ALL_AVAILABLE_FEATURES: %s\n", ALL_AVAILABLE_FEATURES);
158                 services = new ArrayList<>(mRunningServices);
159             }
160             pw.printf(" Number of running services: %d\n", services.size());
161             int i = 0;
162 
163             for (CarServiceBase service : services) {
164                 writer.printf("%d:\n", i);
165                 pw.increaseIndent();
166                 service.dump(pw);
167                 pw.decreaseIndent();
168                 i++;
169             }
170         }
171     }
172 
173     @Nullable
getClassNameForFeature(String featureName)174     private String getClassNameForFeature(String featureName) {
175         switch (featureName) {
176             case ExperimentalCar.TEST_EXPERIMENTAL_FEATURE_SERVICE:
177                 return CarTestDemoExperimentalFeatureManager.class.getName();
178             case ExperimentalCar.DRIVER_DISTRACTION_EXPERIMENTAL_FEATURE_SERVICE:
179                 return CarDriverDistractionManager.class.getName();
180             default:
181                 return null;
182         }
183     }
184 
185     @Nullable
constructServiceForFeature(String featureName)186     private CarServiceBase constructServiceForFeature(String featureName) {
187         switch (featureName) {
188             case ExperimentalCar.TEST_EXPERIMENTAL_FEATURE_SERVICE:
189                 return new TestDemoExperimentalFeatureService();
190             case ExperimentalCar.DRIVER_DISTRACTION_EXPERIMENTAL_FEATURE_SERVICE:
191                 return new DriverDistractionExperimentalFeatureService(mContext);
192             default:
193                 return null;
194         }
195     }
196 
assertCallingFromSystemProcessOrSelf()197     private static void assertCallingFromSystemProcessOrSelf() {
198         int uid = Binder.getCallingUid();
199         int pid = Binder.getCallingPid();
200         if (uid != Process.SYSTEM_UID && pid != Process.myPid()) {
201             throw new SecurityException("Only allowed from system or self, uid:" + uid
202                     + " pid:" + pid);
203         }
204     }
205 
206     /**
207      * Assert that a permission has been granted for the current context.
208      */
assertPermission(Context context, String permission)209     public static void assertPermission(Context context, String permission) {
210         if (context.checkCallingOrSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
211             throw new SecurityException("requires " + permission);
212         }
213     }
214 }
215