@@ -73,7 +73,30 @@ class BrokerDiscoveryClient(private val brokerCandidates: Set<BrokerData>,
7373 val dispatcher = Dispatchers .IO .limitedParallelism(10 )
7474
7575 const val ACTIVE_BROKER_PACKAGE_NAME_BUNDLE_KEY = " ACTIVE_BROKER_PACKAGE_NAME_BUNDLE_KEY"
76- const val ACTIVE_BROKER_SIGNING_CERTIFICATE_THUMBPRINT_BUNDLE_KEY = " ACTIVE_BROKER_SIGNING_CERTIFICATE_THUMBPRINT_BUNDLE_KEY"
76+ const val ACTIVE_BROKER_SIGNING_CERTIFICATE_THUMBPRINT_BUNDLE_KEY =
77+ " ACTIVE_BROKER_SIGNING_CERTIFICATE_THUMBPRINT_BUNDLE_KEY"
78+
79+ const val FORCE_TRIGGER_BROKER_DISCOVERY_BUNDLE_KEY =
80+ " FORCE_TRIGGER_BROKER_DISCOVERY_BUNDLE_KEY"
81+ const val FORCE_TRIGGER_BROKER_DISCOVERY_RESULT_EXECUTED_BUNDLE_KEY =
82+ " FORCE_TRIGGER_BROKER_DISCOVERY_RESULT_EXECUTED_BUNDLE_KEY"
83+
84+ // the broker service is too old and doesn't support this API.
85+ const val FORCE_TRIGGER_BROKER_DISCOVERY_RESULT_OPERATION_NOT_SUPPORTED =
86+ " OPERATION_NOT_SUPPORTED"
87+
88+ // the broker service recognizes this API, but the feature is disabled (e.g. behind a flight).
89+ const val FORCE_TRIGGER_BROKER_DISCOVERY_RESULT_OPERATION_DISABLED = " OPERATION_DISABLED"
90+
91+ // the broker app you're communicating to is not installed.
92+ const val FORCE_TRIGGER_BROKER_DISCOVERY_PACKAGE_NOT_INSTALLED = " PACKAGE_NOT_INSTALLED"
93+
94+ // the broker service you're communicating to is not a valid broker app. (e.g. not signed by proper keys)
95+ const val FORCE_TRIGGER_BROKER_DISCOVERY_NOT_VALID_BROKER = " NOT_VALID_BROKER"
96+
97+ // Unexpected error.
98+ const val FORCE_TRIGGER_BROKER_DISCOVERY_RESULT_UNEXPECTED_ERROR = " UNEXPECTED_ERROR"
99+
77100 const val ERROR_BUNDLE_KEY = " ERROR_BUNDLE_KEY"
78101
79102 /* *
@@ -93,13 +116,15 @@ class BrokerDiscoveryClient(private val brokerCandidates: Set<BrokerData>,
93116 * @param shouldStopQueryForAWhile a method which, if invoked, will force [BrokerDiscoveryClient]
94117 * to skip the IPC discovery process for a while.
95118 **/
96- internal suspend fun queryFromBroker (brokerCandidates : Set <BrokerData >,
97- ipcStrategy : IIpcStrategy ,
98- isPackageInstalled : (BrokerData ) -> Boolean ,
99- isValidBroker : (BrokerData ) -> Boolean
119+ internal suspend fun queryFromBroker (
120+ brokerCandidates : Set <BrokerData >,
121+ ipcStrategy : IIpcStrategy ,
122+ isPackageInstalled : (BrokerData ) -> Boolean ,
123+ isValidBroker : (BrokerData ) -> Boolean
100124 ): BrokerData ? {
101125 return coroutineScope {
102- val installedCandidates = brokerCandidates.filter(isPackageInstalled).filter(isValidBroker)
126+ val installedCandidates =
127+ brokerCandidates.filter(isPackageInstalled).filter(isValidBroker)
103128 val deferredResults = installedCandidates.map { candidate ->
104129 async(dispatcher) {
105130 return @async makeRequest(candidate, ipcStrategy)
@@ -109,8 +134,10 @@ class BrokerDiscoveryClient(private val brokerCandidates: Set<BrokerData>,
109134 }
110135 }
111136
112- private fun makeRequest (candidate : BrokerData ,
113- ipcStrategy : IIpcStrategy ): BrokerData ? {
137+ private fun makeRequest (
138+ candidate : BrokerData ,
139+ ipcStrategy : IIpcStrategy
140+ ): BrokerData ? {
114141 val methodTag = " $TAG :makeRequest"
115142 val operationBundle = BrokerOperationBundle (
116143 BrokerOperationBundle .Operation .BROKER_DISCOVERY_FROM_SDK ,
@@ -120,19 +147,26 @@ class BrokerDiscoveryClient(private val brokerCandidates: Set<BrokerData>,
120147
121148 return try {
122149 val result = ipcStrategy.communicateToBroker(operationBundle)
123- extractResult(result)
150+ extractResult(result, forceTriggerDiscoveryFlow = false )
124151 } catch (t: Throwable ) {
125152 if (t is BrokerCommunicationException &&
126- BrokerCommunicationException .Category .OPERATION_NOT_SUPPORTED_ON_SERVER_SIDE == t.category) {
127- Logger .info(methodTag,
128- " Tried broker discovery on ${candidate} . It doesn't support the IPC mechanism." )
129- } else if (t is ClientException && ONLY_SUPPORTS_ACCOUNT_MANAGER_ERROR_CODE == t.errorCode){
130- Logger .info(methodTag,
153+ BrokerCommunicationException .Category .OPERATION_NOT_SUPPORTED_ON_SERVER_SIDE == t.category
154+ ) {
155+ Logger .info(
156+ methodTag,
157+ " Tried broker discovery on ${candidate} . It doesn't support the IPC mechanism."
158+ )
159+ } else if (t is ClientException && ONLY_SUPPORTS_ACCOUNT_MANAGER_ERROR_CODE == t.errorCode) {
160+ Logger .info(
161+ methodTag,
131162 " Tried broker discovery on ${candidate} . " +
132- " The Broker side indicates that only AccountManager is supported." )
163+ " The Broker side indicates that only AccountManager is supported."
164+ )
133165 } else {
134- Logger .error(methodTag,
135- " Tried broker discovery on ${candidate} , get an error" , t)
166+ Logger .error(
167+ methodTag,
168+ " Tried broker discovery on ${candidate} , get an error" , t
169+ )
136170 }
137171 null
138172 }
@@ -142,7 +176,8 @@ class BrokerDiscoveryClient(private val brokerCandidates: Set<BrokerData>,
142176 * Extract the result returned via the IPC operation
143177 **/
144178 @Throws(NoSuchElementException ::class )
145- private fun extractResult (bundle : Bundle ? ): BrokerData ? {
179+ private fun extractResult (bundle : Bundle ? ,
180+ forceTriggerDiscoveryFlow : Boolean ): BrokerData ? {
146181 if (bundle == null ) {
147182 return null
148183 }
@@ -152,33 +187,92 @@ class BrokerDiscoveryClient(private val brokerCandidates: Set<BrokerData>,
152187 throw errorData as Throwable
153188 }
154189
155- val pkgName = bundle.getString(ACTIVE_BROKER_PACKAGE_NAME_BUNDLE_KEY )? :
156- throw NoSuchElementException (" ACTIVE_BROKER_PACKAGE_NAME_BUNDLE_KEY must not be null" )
190+ if (forceTriggerDiscoveryFlow &&
191+ ! bundle.containsKey(FORCE_TRIGGER_BROKER_DISCOVERY_RESULT_EXECUTED_BUNDLE_KEY )) {
192+ throw ClientException (
193+ FORCE_TRIGGER_BROKER_DISCOVERY_RESULT_OPERATION_NOT_SUPPORTED ,
194+ " Force Broker Discovery is not supported by the broker side. Please update the app."
195+ )
196+ }
197+
198+ val pkgName = bundle.getString(ACTIVE_BROKER_PACKAGE_NAME_BUNDLE_KEY )
199+ ? : throw NoSuchElementException (" ACTIVE_BROKER_PACKAGE_NAME_BUNDLE_KEY must not be null" )
157200
158- val signatureHash = bundle.getString(ACTIVE_BROKER_SIGNING_CERTIFICATE_THUMBPRINT_BUNDLE_KEY )? :
159- throw NoSuchElementException (" ACTIVE_BROKER_SIGNING_CERTIFICATE_THUMBPRINT_BUNDLE_KEY must not be null" )
201+ val signatureHash =
202+ bundle.getString(ACTIVE_BROKER_SIGNING_CERTIFICATE_THUMBPRINT_BUNDLE_KEY )
203+ ? : throw NoSuchElementException (" ACTIVE_BROKER_SIGNING_CERTIFICATE_THUMBPRINT_BUNDLE_KEY must not be null" )
160204
161205 return BrokerData (pkgName, signatureHash)
162206 }
163207 }
164208
165209 constructor (context: Context ,
166210 components: IPlatformComponents ,
167- cache: IClientActiveBrokerCache ): this (
211+ cache: IClientActiveBrokerCache ) : this (
168212 brokerCandidates = BrokerData .getKnownBrokerApps(),
169213 getActiveBrokerFromAccountManager = {
170214 AccountManagerBrokerDiscoveryUtil (context).getActiveBrokerFromAccountManager()
171215 },
172216 ipcStrategy = ContentProviderStrategy (context, components),
173217 cache = cache,
174218 isPackageInstalled = { brokerData ->
175- PackageHelper (context).
176- isPackageInstalledAndEnabled(brokerData.packageName)
219+ PackageHelper (context).isPackageInstalledAndEnabled(brokerData.packageName)
177220 },
178221 isValidBroker = { brokerData ->
179222 BrokerValidator (context).isSignedByKnownKeys(brokerData)
180223 })
181224
225+ @kotlin.jvm.Throws (ClientException ::class )
226+ override fun forceBrokerRediscovery (brokerCandidate : BrokerData ): BrokerData {
227+ val methodTag = " $TAG :forceBrokerRediscovery"
228+ return runBlocking {
229+ classLevelLock.withLock {
230+ try {
231+ if (! isPackageInstalled(brokerCandidate)) {
232+ throw ClientException (
233+ FORCE_TRIGGER_BROKER_DISCOVERY_PACKAGE_NOT_INSTALLED ,
234+ " ${brokerCandidate.packageName} is not installed."
235+ )
236+ }
237+
238+ if (! isValidBroker(brokerCandidate)) {
239+ throw ClientException (
240+ FORCE_TRIGGER_BROKER_DISCOVERY_NOT_VALID_BROKER ,
241+ " ${brokerCandidate.packageName} is not signed with valid key."
242+ )
243+ }
244+
245+ val operationBundle = BrokerOperationBundle (
246+ BrokerOperationBundle .Operation .BROKER_DISCOVERY_FROM_SDK ,
247+ brokerCandidate.packageName,
248+ Bundle ().apply {
249+ putBoolean(FORCE_TRIGGER_BROKER_DISCOVERY_BUNDLE_KEY , true )
250+ }
251+ )
252+
253+ val bundleResult = ipcStrategy.communicateToBroker(operationBundle)
254+ val result = extractResult(bundleResult, forceTriggerDiscoveryFlow = true )
255+ ? : throw ClientException (
256+ FORCE_TRIGGER_BROKER_DISCOVERY_RESULT_UNEXPECTED_ERROR ,
257+ " Result bundle should not be null."
258+ )
259+ cache.setCachedActiveBroker(result)
260+ return @runBlocking result
261+ } catch (c: ClientException ) {
262+ Logger .error(methodTag, " forceBrokerRediscovery Failed." , c)
263+ throw c
264+ } catch (t: Throwable ) {
265+ Logger .error(methodTag, " forceBrokerRediscovery Failed with unknown error." , t)
266+ throw ClientException (
267+ FORCE_TRIGGER_BROKER_DISCOVERY_RESULT_UNEXPECTED_ERROR ,
268+ " Unexpected result: ${t.message} " ,
269+ t
270+ )
271+ }
272+ }
273+ }
274+ }
275+
182276 override fun getActiveBroker (shouldSkipCache : Boolean ): BrokerData ? {
183277 return runBlocking {
184278 return @runBlocking getActiveBrokerAsync(shouldSkipCache)
0 commit comments