1 /*
2  * Copyright (C) 2018 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.dialer.simulator.service;
18 
19 import android.app.ActivityManager;
20 import android.app.ActivityManager.RunningAppProcessInfo;
21 import android.app.Service;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.pm.PackageInfo;
25 import android.content.pm.PackageManager;
26 import android.content.pm.PackageManager.NameNotFoundException;
27 import android.content.pm.Signature;
28 import android.os.Binder;
29 import android.os.IBinder;
30 import android.os.Process;
31 import android.os.RemoteException;
32 import android.support.annotation.Nullable;
33 import com.android.dialer.simulator.impl.SimulatorMainPortal;
34 import com.google.common.base.Optional;
35 import com.google.common.collect.ImmutableList;
36 import java.security.MessageDigest;
37 import java.security.NoSuchAlgorithmException;
38 
39 /**
40  * A secured android service that gives clients simulator api access through binder if clients do
41  * have registered certificates.
42  */
43 public class SimulatorService extends Service {
44 
45   private static final String POPULATE_DATABASE = "Populate database";
46   private static final String CLEAN_DATABASE = "Clean database";
47   private static final String ENABLE_SIMULATOR_MODE = "Enable simulator mode";
48   private static final String DISABLE_SIMULATOR_MODE = "Disable simulator mode";
49   private static final String VOICECALL = "VoiceCall";
50   private static final String NOTIFICATIONS = "Notifications";
51   private static final String CUSTOMIZED_INCOMING_CALL = "Customized incoming call";
52   private static final String CUSTOMIZED_OUTGOING_CALL = "Customized outgoing call";
53   private static final String INCOMING_ENRICHED_CALL = "Incoming enriched call";
54   private static final String OUTGOING_ENRICHED_CALL = "Outgoing enriched call";
55   private static final String MISSED_CALL = "Missed calls (few)";
56 
57   // Certificates that used for checking whether a client is a trusted client.
58   // To get a hashed certificate
59   private ImmutableList<String> certificates;
60 
61   private SimulatorMainPortal simulatorMainPortal;
62 
63   /**
64    * The implementation of {@link ISimulatorService} that contains logic for clients to call
65    * simulator api.
66    */
67   private final ISimulatorService.Stub binder =
68       new ISimulatorService.Stub() {
69 
70         @Override
71         public void makeIncomingCall(String callerId, int presentation) {
72           doSecurityCheck(
73               () -> {
74                 simulatorMainPortal.setCallerId(callerId);
75                 simulatorMainPortal.setPresentation(presentation);
76                 simulatorMainPortal.execute(new String[] {VOICECALL, CUSTOMIZED_INCOMING_CALL});
77               });
78         }
79 
80         @Override
81         public void makeOutgoingCall(String callerId, int presentation) {
82           doSecurityCheck(
83               () -> {
84                 simulatorMainPortal.setCallerId(callerId);
85                 simulatorMainPortal.setPresentation(presentation);
86                 simulatorMainPortal.execute(new String[] {VOICECALL, CUSTOMIZED_OUTGOING_CALL});
87               });
88         }
89 
90         @Override
91         public void populateDataBase() throws RemoteException {
92           doSecurityCheck(
93               () -> {
94                 simulatorMainPortal.execute(new String[] {POPULATE_DATABASE});
95               });
96         }
97 
98         @Override
99         public void cleanDataBase() throws RemoteException {
100           doSecurityCheck(
101               () -> {
102                 simulatorMainPortal.execute(new String[] {CLEAN_DATABASE});
103               });
104         }
105 
106         @Override
107         public void enableSimulatorMode() throws RemoteException {
108           doSecurityCheck(
109               () -> {
110                 simulatorMainPortal.execute(new String[] {ENABLE_SIMULATOR_MODE});
111               });
112         }
113 
114         @Override
115         public void disableSimulatorMode() throws RemoteException {
116           doSecurityCheck(
117               () -> {
118                 simulatorMainPortal.execute(new String[] {DISABLE_SIMULATOR_MODE});
119               });
120         }
121 
122         @Override
123         public void makeIncomingEnrichedCall() throws RemoteException {
124           doSecurityCheck(
125               () -> {
126                 simulatorMainPortal.execute(new String[] {VOICECALL, INCOMING_ENRICHED_CALL});
127               });
128         }
129 
130         @Override
131         public void makeOutgoingEnrichedCall() throws RemoteException {
132           doSecurityCheck(
133               () -> {
134                 simulatorMainPortal.execute(new String[] {VOICECALL, OUTGOING_ENRICHED_CALL});
135               });
136         }
137 
138         @Override
139         public void populateMissedCall(int num) throws RemoteException {
140           doSecurityCheck(
141               () -> {
142                 simulatorMainPortal.setMissedCallNum(num);
143                 simulatorMainPortal.execute(new String[] {NOTIFICATIONS, MISSED_CALL});
144               });
145         }
146 
147         private void doSecurityCheck(Runnable runnable) {
148           if (!hasAccessToService()) {
149             throw new RuntimeException("Client doesn't have access to Simulator service!");
150           }
151           runnable.run();
152         }
153       };
154 
155   /** Sets SimulatorMainPortal instance for SimulatorService. */
setSimulatorMainPortal(SimulatorMainPortal simulatorMainPortal)156   public void setSimulatorMainPortal(SimulatorMainPortal simulatorMainPortal) {
157     this.simulatorMainPortal = simulatorMainPortal;
158   }
159 
160   /** Sets immutable CertificatesList for SimulatorService. */
setCertificatesList(ImmutableList<String> certificates)161   public void setCertificatesList(ImmutableList<String> certificates) {
162     this.certificates = certificates;
163   }
164 
hasAccessToService()165   private boolean hasAccessToService() {
166     int clientPid = Binder.getCallingPid();
167     if (clientPid == Process.myPid()) {
168       throw new RuntimeException("Client and service have the same PID!");
169     }
170     Optional<String> packageName = getPackageNameForPid(clientPid);
171     if (packageName.isPresent()) {
172       try {
173         PackageInfo packageInfo =
174             getPackageManager().getPackageInfo(packageName.get(), PackageManager.GET_SIGNATURES);
175         MessageDigest messageDigest = MessageDigest.getInstance("MD5");
176         if (packageInfo.signatures.length != 1) {
177           throw new NotOnlyOneSignatureException("The client has more than one signature!");
178         }
179         Signature signature = packageInfo.signatures[0];
180         return isCertificateValid(messageDigest.digest(signature.toByteArray()), this.certificates);
181       } catch (NameNotFoundException | NoSuchAlgorithmException | NotOnlyOneSignatureException e) {
182         throw new RuntimeException(e);
183       }
184     }
185     return false;
186   }
187 
getPackageNameForPid(int pid)188   private Optional<String> getPackageNameForPid(int pid) {
189     ActivityManager activityManager =
190         (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
191     for (RunningAppProcessInfo processInfo : activityManager.getRunningAppProcesses()) {
192       if (processInfo.pid == pid) {
193         return Optional.of(processInfo.processName);
194       }
195     }
196     return Optional.absent();
197   }
198 
isCertificateValid( byte[] clientCerfificate, ImmutableList<String> certificates)199   private static boolean isCertificateValid(
200       byte[] clientCerfificate, ImmutableList<String> certificates) {
201     for (String certificate : certificates) {
202       if (certificate.equals(bytesToHexString(clientCerfificate))) {
203         return true;
204       }
205     }
206     return false;
207   }
208 
bytesToHexString(byte[] in)209   private static String bytesToHexString(byte[] in) {
210     final StringBuilder builder = new StringBuilder();
211     for (byte b : in) {
212       builder.append(String.format("%02X", b));
213     }
214     return builder.toString();
215   }
216 
217   @Nullable
218   @Override
onBind(Intent intent)219   public IBinder onBind(Intent intent) {
220     return binder;
221   }
222 
223   private static class NotOnlyOneSignatureException extends Exception {
NotOnlyOneSignatureException(String desc)224     public NotOnlyOneSignatureException(String desc) {
225       super(desc);
226     }
227   }
228 }
229