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.libraries.rcs.simpleclient; 18 19 import android.os.Build.VERSION_CODES; 20 import android.telephony.ims.ImsException; 21 import android.util.Log; 22 23 import androidx.annotation.RequiresApi; 24 25 import com.android.libraries.rcs.simpleclient.protocol.sip.SipSession; 26 import com.android.libraries.rcs.simpleclient.provisioning.ProvisioningController; 27 import com.android.libraries.rcs.simpleclient.registration.RegistrationController; 28 import com.android.libraries.rcs.simpleclient.registration.RegistrationStateChangeCallback; 29 import com.android.libraries.rcs.simpleclient.service.ImsService; 30 31 import java.util.concurrent.atomic.AtomicReference; 32 33 /** 34 * Simple RCS client implementation. 35 * 36 * State is covered by a context instance. 37 */ 38 @RequiresApi(api = VERSION_CODES.R) 39 public class SimpleRcsClient { 40 private static final String TAG = SimpleRcsClient.class.getSimpleName(); 41 private final AtomicReference<State> state = new AtomicReference<>(State.NEW); 42 private ProvisioningController provisioningController; 43 private RegistrationController registrationController; 44 private ImsService imsService; 45 private SimpleRcsClientContext context; 46 private StateChangedCallback stateChangedCallback; 47 newBuilder()48 public static Builder newBuilder() { 49 return new Builder(); 50 } 51 getContext()52 public SimpleRcsClientContext getContext() { 53 return context; 54 } 55 start()56 public void start() { 57 provision(); 58 } 59 stop()60 public void stop() { 61 Log.i(TAG, "stop.."); 62 registrationController.deregister(); 63 provisioningController.unRegister(); 64 provisioningController = null; 65 registrationController = null; 66 imsService = null; 67 } 68 onStateChanged(StateChangedCallback cb)69 public void onStateChanged(StateChangedCallback cb) { 70 this.stateChangedCallback = cb; 71 } 72 enterState(State expected, State newState)73 private boolean enterState(State expected, State newState) { 74 boolean result = state.compareAndSet(expected, newState); 75 76 if (result && stateChangedCallback != null) { 77 try { 78 stateChangedCallback.notifyStateChange(expected, newState); 79 } catch (Exception e) { 80 Log.e(TAG, "Exception on calling state change callback", e); 81 } 82 } 83 Log.i(TAG, "expected:" + expected + " new:" + newState + " res:" + result); 84 return result; 85 } 86 provision()87 private void provision() { 88 if (!enterState(State.NEW, State.PROVISIONING)) { 89 return; 90 } 91 provisioningController.onConfigurationChange(configXml -> { 92 register(); 93 }); 94 try { 95 provisioningController.triggerProvisioning(); 96 } catch (ImsException e) { 97 // TODO: ... 98 } 99 } 100 register()101 private void register() { 102 if (!enterState(State.PROVISIONING, State.REGISTERING)) { 103 return; 104 } 105 106 registrationController.register(imsService, 107 new RegistrationStateChangeCallback() { 108 @Override 109 public void notifyRegStateChanged(ImsService imsService) { 110 111 } 112 113 @Override 114 public void onSuccess(SipSession sipSession) { 115 Log.i(TAG, "onSuccess"); 116 registered(sipSession); 117 } 118 119 @Override 120 public void onFailure(String reason) { 121 Log.i(TAG, "onFailure reason:" + reason); 122 notRegistered(); 123 } 124 }); 125 } 126 registered(SipSession session)127 private void registered(SipSession session) { 128 if (state.get().equals(State.REGISTERING) || state.get().equals(State.NOT_REGISTERED)) { 129 enterState(state.get(), State.REGISTERED); 130 } 131 132 context = new SimpleRcsClientContext(provisioningController, registrationController, 133 imsService, 134 session); 135 136 imsService.start(context); 137 } 138 notRegistered()139 private void notRegistered() { 140 enterState(State.REGISTERED, State.NOT_REGISTERED); 141 } 142 143 /** 144 * Possible client states. 145 */ 146 public enum State { 147 NEW, 148 PROVISIONING, 149 REGISTERING, 150 REGISTERED, 151 NOT_REGISTERED, 152 } 153 154 /** 155 * Builder for creating new SimpleRcsClient instances. 156 */ 157 public static class Builder { 158 159 private ProvisioningController provisioningController; 160 private RegistrationController registrationController; 161 private ImsService imsService; 162 provisioningController(ProvisioningController controller)163 public Builder provisioningController(ProvisioningController controller) { 164 this.provisioningController = controller; 165 return this; 166 } 167 registrationController(RegistrationController controller)168 public Builder registrationController(RegistrationController controller) { 169 this.registrationController = controller; 170 return this; 171 } 172 imsService(ImsService imsService)173 public Builder imsService(ImsService imsService) { 174 this.imsService = imsService; 175 return this; 176 } 177 build()178 public SimpleRcsClient build() { 179 SimpleRcsClient client = new SimpleRcsClient(); 180 client.registrationController = registrationController; 181 client.provisioningController = provisioningController; 182 client.imsService = imsService; 183 184 return client; 185 } 186 } 187 } 188