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