1 /*
2  * Copyright (C) 2020 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.settings.network.telephony;
18 
19 import android.Manifest;
20 import android.app.PendingIntent;
21 import android.content.BroadcastReceiver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.os.Bundle;
26 import android.os.SystemClock;
27 import android.telephony.TelephonyManager;
28 import android.telephony.euicc.EuiccManager;
29 import android.util.Log;
30 
31 import com.android.settings.SidecarFragment;
32 import com.android.settings.network.SwitchSlotSidecar;
33 
34 import java.util.concurrent.atomic.AtomicInteger;
35 
36 /**
37  * The sidecar base class that an Euicc sidecar can extend from. The extended class should implement
38  * getReceiverAction() to return the action string for the broadcast receiver. The extended class
39  * should implement its own get() function to return an instance of that class, and implement the
40  * functional class like run() to actually trigger the function in EuiccManager.
41  */
42 public abstract class EuiccOperationSidecar extends SidecarFragment
43         implements SidecarFragment.Listener{
44     private static final String TAG = "EuiccOperationSidecar";
45     private static final int REQUEST_CODE = 0;
46     private static final String EXTRA_OP_ID = "op_id";
47     private static AtomicInteger sCurrentOpId =
48             new AtomicInteger((int) SystemClock.elapsedRealtime());
49 
50     protected EuiccManager mEuiccManager;
51     protected TelephonyManager mTelephonyManager;
52     protected SwitchSlotSidecar mSwitchSlotSidecar;
53 
54 
55     private int mResultCode;
56     private int mDetailedCode;
57     private Intent mResultIntent;
58     private int mOpId;
59 
60     protected final BroadcastReceiver mReceiver =
61             new BroadcastReceiver() {
62                 @Override
63                 public void onReceive(Context context, Intent intent) {
64                     if (getReceiverAction().equals(intent.getAction())
65                             && mOpId == intent.getIntExtra(EXTRA_OP_ID, -1)) {
66                         mResultCode = getResultCode();
67                         /* TODO: This relies on our LUI and LPA to coexist, should think about how
68                         to generalize this further. */
69                         mDetailedCode =
70                                 intent.getIntExtra(
71                                         EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE,
72                                         0 /* defaultValue*/);
73                         mResultIntent = intent;
74                         Log.i(
75                                 TAG,
76                                 String.format(
77                                         "Result code : %d; detailed code : %d",
78                                         mResultCode, mDetailedCode));
79                         onActionReceived();
80                     }
81                 }
82             };
83 
84     /**
85      * This is called when the broadcast action is received. The subclass may override this to
86      * perform different logic. The broadcast result code may be obtained with {@link
87      * #getResultCode()} and the Intent may be obtained with {@link #getResultIntent()}.
88      */
onActionReceived()89     protected void onActionReceived() {
90         if (mResultCode == EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) {
91             setState(State.SUCCESS, Substate.UNUSED);
92         } else {
93             setState(State.ERROR, mResultCode);
94         }
95     }
96 
97     /**
98      * The extended class should implement it to return a string for the broadcast action. The class
99      * should be unique across all the child classes.
100      */
getReceiverAction()101     protected abstract String getReceiverAction();
102 
createCallbackIntent()103     protected PendingIntent createCallbackIntent() {
104         mOpId = sCurrentOpId.incrementAndGet();
105         Intent intent = new Intent(getReceiverAction());
106         intent.putExtra(EXTRA_OP_ID, mOpId);
107         return PendingIntent.getBroadcast(
108                 getContext(), REQUEST_CODE, intent,
109                 PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
110     }
111 
112     @Override
onCreate(Bundle savedInstanceState)113     public void onCreate(Bundle savedInstanceState) {
114         super.onCreate(savedInstanceState);
115         mEuiccManager = getContext().getSystemService(EuiccManager.class);
116         mTelephonyManager = getContext().getSystemService(TelephonyManager.class);
117         mSwitchSlotSidecar = SwitchSlotSidecar.get(getChildFragmentManager());
118 
119         getContext()
120                 .getApplicationContext()
121                 .registerReceiver(
122                         mReceiver,
123                         new IntentFilter(getReceiverAction()),
124                         Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS,
125                         null,
126                         Context.RECEIVER_EXPORTED);
127     }
128 
129     @Override
onResume()130     public void onResume() {
131         super.onResume();
132         mSwitchSlotSidecar.addListener(this);
133     }
134 
135     @Override
onPause()136     public void onPause() {
137         mSwitchSlotSidecar.removeListener(this);
138         super.onPause();
139     }
140 
141     @Override
onDestroy()142     public void onDestroy() {
143         getContext().getApplicationContext().unregisterReceiver(mReceiver);
144         super.onDestroy();
145     }
146 
147     @Override
onStateChange(SidecarFragment fragment)148     public void onStateChange(SidecarFragment fragment) {
149         if (fragment == mSwitchSlotSidecar) {
150             switch (mSwitchSlotSidecar.getState()) {
151                 case State.SUCCESS:
152                     mSwitchSlotSidecar.reset();
153                     Log.i(TAG, "mSwitchSlotSidecar SUCCESS");
154                     break;
155                 case State.ERROR:
156                     mSwitchSlotSidecar.reset();
157                     Log.i(TAG, "mSwitchSlotSidecar ERROR");
158                     break;
159             }
160         } else {
161             Log.wtf(TAG, "Received state change from a sidecar not expected.");
162         }
163     }
164 
getResultCode()165     public int getResultCode() {
166         return mResultCode;
167     }
168 
getDetailedCode()169     public int getDetailedCode() {
170         return mDetailedCode;
171     }
172 
getResultIntent()173     public Intent getResultIntent() {
174         return mResultIntent;
175     }
176 }
177