1 /*
2  * Copyright (C) 2017 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.phone.otasp;
17 
18 import android.app.Service;
19 import android.content.Context;
20 import android.content.Intent;
21 import android.os.AsyncResult;
22 import android.os.Handler;
23 import android.os.IBinder;
24 import android.os.Message;
25 import android.telephony.ServiceState;
26 import android.telephony.SubscriptionManager;
27 import android.telephony.TelephonyManager;
28 
29 import com.android.internal.telephony.Phone;
30 import com.android.internal.telephony.PhoneConstants;
31 import com.android.phone.PhoneGlobals;
32 import com.android.phone.PhoneUtils;
33 
34 import static com.android.phone.PhoneGlobals.getPhone;
35 
36 /**
37  * otasp activation service handles all logic related with OTASP call.
38  * OTASP is a CDMA-specific feature: OTA or OTASP == Over The Air service provisioning
39  * In practice, in a normal successful OTASP call, events come in as follows:
40  * - SPL_UNLOCKED within a couple of seconds after the call starts
41  * - PRL_DOWNLOADED and MDN_DOWNLOADED and COMMITTED within a span of 2 seconds
42  * - poll cdma subscription from RIL after COMMITTED
43  * - SIM reloading with provisioned MDN and MIN
44  */
45 public class OtaspActivationService extends Service {
46     private static final String TAG = OtaspActivationService.class.getSimpleName();
47     private static final boolean DBG = true;
48     /* non-interactive otasp number */
49     private static final String OTASP_NUMBER = "*22899";
50 
51     /**
52      * Otasp call follows with SIM reloading which might triggers a retry loop on activation
53      * failure. A max retry limit could help prevent retry loop.
54      */
55     private static final int OTASP_CALL_RETRIES_MAX = 3;
56     private static final int OTASP_CALL_RETRY_PERIOD_IN_MS = 3000;
57     private static int sOtaspCallRetries = 0;
58 
59     /* events */
60     private static final int EVENT_CALL_STATE_CHANGED                     = 0;
61     private static final int EVENT_CDMA_OTASP_CALL_RETRY                  = 1;
62     private static final int EVENT_CDMA_PROVISION_STATUS_UPDATE           = 2;
63     private static final int EVENT_SERVICE_STATE_CHANGED                  = 3;
64     private static final int EVENT_START_OTASP_CALL                       = 4;
65 
66     /* use iccid to detect hot sim swap */
67     private static String sIccId = null;
68 
69     private Phone mPhone;
70     /* committed flag indicates Otasp call succeed */
71     private boolean mIsOtaspCallCommitted = false;
72 
73     @Override
onCreate()74     public void onCreate() {
75         logd("otasp service onCreate");
76         mPhone = PhoneGlobals.getPhone();
77         if ((sIccId == null) || !sIccId.equals(mPhone.getIccSerialNumber())) {
78             // reset to allow activation retry on new sim
79             sIccId = mPhone.getIccSerialNumber();
80             sOtaspCallRetries = 0;
81         }
82         sOtaspCallRetries++;
83         logd("OTASP call tried " + sOtaspCallRetries + " times");
84         if (sOtaspCallRetries > OTASP_CALL_RETRIES_MAX) {
85             logd("OTASP call exceeds max retries => activation failed");
86             updateActivationState(this, false);
87             onComplete();
88             return;
89         }
90         mHandler.sendEmptyMessage(EVENT_START_OTASP_CALL);
91     }
92 
93     @Override
onStartCommand(Intent intent, int flags, int startId)94     public int onStartCommand(Intent intent, int flags, int startId) {
95         return START_REDELIVER_INTENT;
96     }
97 
98     @Override
onBind(Intent intent)99     public IBinder onBind(Intent intent) {
100         return null;
101     }
102 
103     private Handler mHandler = new Handler() {
104         @Override
105         public void handleMessage(Message msg) {
106             switch (msg.what) {
107                 case EVENT_SERVICE_STATE_CHANGED:
108                     logd("EVENT_SERVICE_STATE_CHANGED");
109                     onStartOtaspCall();
110                     break;
111                 case EVENT_START_OTASP_CALL:
112                     logd("EVENT_START_OTASP_CALL");
113                     onStartOtaspCall();
114                     break;
115                 case EVENT_CALL_STATE_CHANGED:
116                     logd("OTASP_CALL_STATE_CHANGED");
117                     onOtaspCallStateChanged();
118                     break;
119                 case EVENT_CDMA_PROVISION_STATUS_UPDATE:
120                     logd("OTASP_ACTIVATION_STATUS_UPDATE_EVENT");
121                     onCdmaProvisionStatusUpdate((AsyncResult) msg.obj);
122                     break;
123                 case EVENT_CDMA_OTASP_CALL_RETRY:
124                     logd("EVENT_CDMA_OTASP_CALL_RETRY");
125                     onStartOtaspCall();
126                     break;
127                 default:
128                     loge("invalid msg: " + msg.what + " not handled.");
129             }
130         }
131     };
132 
133     /**
134      * Starts the OTASP call without any UI.
135      * platform only support background non-interactive otasp call, but users could still dial
136      * interactive OTASP number through dialer if carrier allows (some carrier will
137      * explicitly block any outgoing *288XX number).
138      */
onStartOtaspCall()139     private void onStartOtaspCall() {
140         unregisterAll();
141         if (mPhone.getServiceState().getState() != ServiceState.STATE_IN_SERVICE) {
142             loge("OTASP call failure, wait for network available.");
143             mPhone.registerForServiceStateChanged(mHandler, EVENT_SERVICE_STATE_CHANGED, null);
144             return;
145         }
146         // otasp call follows with CDMA OTA PROVISION STATUS update which signals activation result
147         mPhone.registerForCdmaOtaStatusChange(mHandler, EVENT_CDMA_PROVISION_STATUS_UPDATE, null);
148         mPhone.registerForPreciseCallStateChanged(mHandler, EVENT_CALL_STATE_CHANGED, null);
149         logd("startNonInteractiveOtasp: placing call to '" + OTASP_NUMBER + "'...");
150         int callStatus = PhoneUtils.placeCall(this,
151                 getPhone(),
152                 OTASP_NUMBER,
153                 null,   // contactRef
154                 false); // isEmergencyCall
155         if (callStatus == PhoneUtils.CALL_STATUS_DIALED) {
156             if (DBG) logd("  ==> success return from placeCall(): callStatus = " + callStatus);
157         } else {
158             loge(" ==> failure return from placeCall(): callStatus = " + callStatus);
159             mHandler.sendEmptyMessageDelayed(EVENT_CDMA_OTASP_CALL_RETRY,
160                     OTASP_CALL_RETRY_PERIOD_IN_MS);
161         }
162     }
163 
164     /**
165      * register for cdma ota provision status
166      * see RIL_CDMA_OTA_ProvisionStatus in include/telephony/ril.h
167      */
onCdmaProvisionStatusUpdate(AsyncResult r)168     private void onCdmaProvisionStatusUpdate(AsyncResult r) {
169         int[] otaStatus = (int[]) r.result;
170         logd("onCdmaProvisionStatusUpdate: " + otaStatus[0]);
171         if (Phone.CDMA_OTA_PROVISION_STATUS_COMMITTED == otaStatus[0]) {
172             mIsOtaspCallCommitted = true;
173         }
174     }
175 
176     /**
177      * update activation state upon call disconnected.
178      * check the mIsOtaspCallCommitted bit, and if that's true it means that activation
179      * was successful.
180      */
onOtaspCallStateChanged()181     private void onOtaspCallStateChanged() {
182         logd("onOtaspCallStateChanged: " + mPhone.getState());
183         if (mPhone.getState().equals(PhoneConstants.State.IDLE)) {
184             if (mIsOtaspCallCommitted) {
185                 logd("Otasp activation succeed");
186                 updateActivationState(this, true);
187             } else {
188                 logd("Otasp activation failed");
189                 updateActivationState(this, false);
190             }
191             onComplete();
192         }
193     }
194 
onComplete()195     private void onComplete() {
196         logd("otasp service onComplete");
197         unregisterAll();
198         stopSelf();
199     }
200 
unregisterAll()201     private void unregisterAll() {
202         mPhone.unregisterForCdmaOtaStatusChange(mHandler);
203         mPhone.unregisterForSubscriptionInfoReady(mHandler);
204         mPhone.unregisterForServiceStateChanged(mHandler);
205         mPhone.unregisterForPreciseCallStateChanged(mHandler);
206         mHandler.removeCallbacksAndMessages(null);
207     }
208 
updateActivationState(Context context, boolean success)209     public static void updateActivationState(Context context, boolean success) {
210         final TelephonyManager mTelephonyMgr = TelephonyManager.from(context);
211         int state = (success) ? TelephonyManager.SIM_ACTIVATION_STATE_ACTIVATED :
212                 TelephonyManager.SIM_ACTIVATION_STATE_DEACTIVATED;
213         int subId = SubscriptionManager.getDefaultSubscriptionId();
214         mTelephonyMgr.setVoiceActivationState(subId, state);
215         mTelephonyMgr.setDataActivationState(subId, state);
216     }
217 
logd(String s)218     private static void logd(String s) {
219         android.util.Log.d(TAG, s);
220     }
221 
loge(String s)222     private static void loge(String s) {
223         android.util.Log.e(TAG, s);
224     }
225 }
226