1 /*
2  * Copyright (C) 2011 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 an
14  * limitations under the License.
15  */
16 
17 package com.android.server;
18 
19 import android.annotation.EnforcePermission;
20 import android.annotation.NonNull;
21 import android.content.Context;
22 import android.hardware.ISerialManager;
23 import android.hardware.SerialManagerInternal;
24 import android.os.ParcelFileDescriptor;
25 import android.os.PermissionEnforcer;
26 
27 import com.android.internal.annotations.GuardedBy;
28 import com.android.internal.util.Preconditions;
29 
30 import java.io.File;
31 import java.util.ArrayList;
32 import java.util.LinkedHashMap;
33 import java.util.function.Supplier;
34 
35 @android.ravenwood.annotation.RavenwoodKeepWholeClass
36 public class SerialService extends ISerialManager.Stub {
37     private final Context mContext;
38 
39     @GuardedBy("mSerialPorts")
40     private final LinkedHashMap<String, Supplier<ParcelFileDescriptor>> mSerialPorts =
41             new LinkedHashMap<>();
42 
43     private static final String PREFIX_VIRTUAL = "virtual:";
44 
SerialService(Context context)45     public SerialService(Context context) {
46         super(PermissionEnforcer.fromContext(context));
47         mContext = context;
48 
49         synchronized (mSerialPorts) {
50             final String[] serialPorts = getSerialPorts(context);
51             for (String serialPort : serialPorts) {
52                 mSerialPorts.put(serialPort, () -> {
53                     return native_open(serialPort);
54                 });
55             }
56         }
57     }
58 
59     @android.ravenwood.annotation.RavenwoodReplace
getSerialPorts(Context context)60     private static String[] getSerialPorts(Context context) {
61         return context.getResources().getStringArray(
62                 com.android.internal.R.array.config_serialPorts);
63     }
64 
getSerialPorts$ravenwood(Context context)65     private static String[] getSerialPorts$ravenwood(Context context) {
66         return new String[0];
67     }
68 
69     public static class Lifecycle extends SystemService {
70         private SerialService mService;
71 
Lifecycle(Context context)72         public Lifecycle(Context context) {
73             super(context);
74         }
75 
76         @Override
onStart()77         public void onStart() {
78             mService = new SerialService(getContext());
79             publishBinderService(Context.SERIAL_SERVICE, mService);
80             publishLocalService(SerialManagerInternal.class, mService.mInternal);
81         }
82     }
83 
84     @EnforcePermission(android.Manifest.permission.SERIAL_PORT)
getSerialPorts()85     public String[] getSerialPorts() {
86         super.getSerialPorts_enforcePermission();
87 
88         synchronized (mSerialPorts) {
89             final ArrayList<String> ports = new ArrayList<>();
90             for (String path : mSerialPorts.keySet()) {
91                 if (path.startsWith(PREFIX_VIRTUAL) || new File(path).exists()) {
92                     ports.add(path);
93                 }
94             }
95             return ports.toArray(new String[ports.size()]);
96         }
97     }
98 
99     @EnforcePermission(android.Manifest.permission.SERIAL_PORT)
openSerialPort(String path)100     public ParcelFileDescriptor openSerialPort(String path) {
101         super.openSerialPort_enforcePermission();
102 
103         synchronized (mSerialPorts) {
104             final Supplier<ParcelFileDescriptor> supplier = mSerialPorts.get(path);
105             if (supplier != null) {
106                 return supplier.get();
107             } else {
108                 throw new IllegalArgumentException("Invalid serial port " + path);
109             }
110         }
111     }
112 
113     private final SerialManagerInternal mInternal = new SerialManagerInternal() {
114         @Override
115         public void addVirtualSerialPortForTest(@NonNull String name,
116                 @NonNull Supplier<ParcelFileDescriptor> supplier) {
117             synchronized (mSerialPorts) {
118                 Preconditions.checkState(!mSerialPorts.containsKey(name),
119                         "Port " + name + " already defined");
120                 Preconditions.checkArgument(name.startsWith(PREFIX_VIRTUAL),
121                         "Port " + name + " must be under " + PREFIX_VIRTUAL);
122                 mSerialPorts.put(name, supplier);
123             }
124         }
125 
126         @Override
127         public void removeVirtualSerialPortForTest(@NonNull String name) {
128             synchronized (mSerialPorts) {
129                 Preconditions.checkState(mSerialPorts.containsKey(name),
130                         "Port " + name + " not yet defined");
131                 Preconditions.checkArgument(name.startsWith(PREFIX_VIRTUAL),
132                         "Port " + name + " must be under " + PREFIX_VIRTUAL);
133                 mSerialPorts.remove(name);
134             }
135         }
136     };
137 
native_open(String path)138     private native ParcelFileDescriptor native_open(String path);
139 }
140