Skip to content

Commit 2e1c14b

Browse files
authored
Merge branch 'dev' into copilot/add-feature-flag-and-telemetry-constants
2 parents c8b5997 + 43ffd79 commit 2e1c14b

52 files changed

Lines changed: 4394 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
vNext
22
----------
3+
- [MINOR] Move device registration protocol types, domain types, controller, and packer from broker to common to enable OneAuth device registration support (#3066)
34
- [MINOR] Upgrade compileSdkVersion to 36 and buildToolsVersion to 36.0.0 (#3065)
45
- [PATCH] Rename SovSG to GovSG for the Singapore sovereign cloud identifiers (#3068)
56

common/consumer-rules.pro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
-keep class * extends com.microsoft.identity.common.java.cache.ITokenCacheItem { *; }
2626
-keep class * extends com.microsoft.identity.common.java.authscheme.AbstractAuthenticationScheme { *; }
2727
-keep class com.microsoft.identity.common.internal.broker.AuthUxJsonPayload { *; }
28+
-keep class * extends com.microsoft.identity.deviceregistration.java.protocol.IDeviceRegistrationProtocol { *; }
29+
-keep class * extends com.microsoft.identity.deviceregistration.java.api.IDeviceRegistrationRecord { *; }
2830

2931
# Keep WebView JS bridge methods annotated with @JavascriptInterface
3032
-keepclassmembers class com.microsoft.identity.** {
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// All rights reserved.
3+
//
4+
// This code is licensed under the MIT License.
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files(the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions :
12+
//
13+
// The above copyright notice and this permission notice shall be included in
14+
// all copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
// THE SOFTWARE.
23+
package com.microsoft.identity.deviceregistration;
24+
25+
import static com.microsoft.identity.common.internal.broker.ipc.BrokerOperationBundle.Operation.DEVICE_REGISTRATION_OPERATIONS;
26+
import static com.microsoft.identity.common.internal.broker.ipc.IIpcStrategy.Type.BOUND_SERVICE;
27+
import static com.microsoft.identity.common.internal.broker.ipc.IIpcStrategy.Type.CONTENT_PROVIDER;
28+
import static com.microsoft.identity.common.internal.broker.ipc.IIpcStrategy.Type.LEGACY_ACCOUNT_AUTHENTICATOR_FOR_WPJ_API;
29+
import static com.microsoft.identity.common.java.exception.ClientException.INVALID_BROKER_BUNDLE;
30+
31+
import android.content.Context;
32+
import android.os.Bundle;
33+
import android.os.Looper;
34+
35+
import androidx.annotation.NonNull;
36+
import androidx.annotation.Nullable;
37+
38+
import com.microsoft.identity.common.exception.BrokerCommunicationException;
39+
import com.microsoft.identity.common.internal.activebrokerdiscovery.IBrokerDiscoveryClient;
40+
import com.microsoft.identity.common.internal.broker.BrokerData;
41+
import com.microsoft.identity.common.internal.broker.ipc.BrokerOperationBundle;
42+
import com.microsoft.identity.common.internal.broker.ipc.IIpcStrategy;
43+
import com.microsoft.identity.common.internal.cache.ActiveBrokerCacheUpdater;
44+
import com.microsoft.identity.deviceregistration.java.api.IDeviceRegistrationClientController;
45+
import com.microsoft.identity.deviceregistration.java.exception.DeviceRegistrationException;
46+
import com.microsoft.identity.deviceregistration.java.protocol.parameters.IDeviceRegistrationProtocolParameters;
47+
import com.microsoft.identity.common.java.exception.BaseException;
48+
import com.microsoft.identity.common.java.exception.ClientException;
49+
import com.microsoft.identity.common.java.interfaces.IPlatformComponents;
50+
import com.microsoft.identity.common.java.opentelemetry.AttributeName;
51+
import com.microsoft.identity.common.java.opentelemetry.OTelUtility;
52+
import com.microsoft.identity.common.java.opentelemetry.SpanExtension;
53+
import com.microsoft.identity.common.java.opentelemetry.SpanName;
54+
import com.microsoft.identity.common.logging.Logger;
55+
56+
import java.util.LinkedList;
57+
import java.util.List;
58+
import java.util.Queue;
59+
60+
import io.opentelemetry.api.trace.Span;
61+
import io.opentelemetry.api.trace.StatusCode;
62+
import io.opentelemetry.context.Scope;
63+
64+
/**
65+
* Android specific device registration controller to communicate protocols to the broker.
66+
* Moved from AADAuthenticator to common module to allow OneAuth consumers to use it.
67+
*/
68+
public class AndroidDeviceRegistrationClientController implements IDeviceRegistrationClientController {
69+
private static final String TAG = AndroidDeviceRegistrationClientController.class.getSimpleName();
70+
71+
@NonNull
72+
private final String mActiveBrokerPackageName;
73+
@NonNull
74+
private final List<IIpcStrategy> mIpcStrategies;
75+
@NonNull
76+
private static final AndroidDeviceRegistrationProtocolPacker mProtocolPacker
77+
= new AndroidDeviceRegistrationProtocolPacker();
78+
79+
private final boolean mSupportsBoundService;
80+
81+
@NonNull
82+
private final ActiveBrokerCacheUpdater mCacheUpdater;
83+
84+
@NonNull
85+
private final String mCallerPackageName;
86+
87+
/**
88+
* Creates a new controller.
89+
*
90+
* @param context application context.
91+
* @param components platform components.
92+
* @param discoveryClient broker discovery client for resolving active broker.
93+
* @param strategiesProvider provides the IPC strategies and supportsBoundService flag.
94+
* @param cacheUpdater cache updater for active broker cache.
95+
*/
96+
public AndroidDeviceRegistrationClientController(
97+
@NonNull final Context context,
98+
@NonNull final IPlatformComponents components,
99+
@NonNull final IBrokerDiscoveryClient discoveryClient,
100+
@NonNull final DeviceRegistrationIpcStrategiesProvider strategiesProvider,
101+
@NonNull final ActiveBrokerCacheUpdater cacheUpdater
102+
) throws ClientException {
103+
mCallerPackageName = context.getPackageName();
104+
mActiveBrokerPackageName = getActiveBrokerPackageName(discoveryClient);
105+
mIpcStrategies = strategiesProvider.getStrategies(context, components, mActiveBrokerPackageName);
106+
mCacheUpdater = cacheUpdater;
107+
mSupportsBoundService = strategiesProvider.getSupportsBoundService();
108+
}
109+
110+
private static String getActiveBrokerPackageName(
111+
@NonNull final IBrokerDiscoveryClient discoveryClient) throws ClientException {
112+
final BrokerData activeBroker = discoveryClient.getActiveBroker(false);
113+
if (activeBroker == null) {
114+
throw new ClientException(ClientException.NOT_VALID_BROKER_FOUND,
115+
"Broker should not be null when invoked from Broker API.");
116+
}
117+
return activeBroker.getPackageName();
118+
}
119+
120+
/**
121+
* Communicates the protocol associated with the given parameters using the available strategies.
122+
*
123+
* @param protocolParameters protocol parameters to execute.
124+
* @return a serialized protocol response.
125+
* @throws BaseException if all strategies to execute the protocol fail.
126+
*/
127+
@Override
128+
@NonNull
129+
public final byte[] execute(@NonNull final IDeviceRegistrationProtocolParameters protocolParameters)
130+
throws BaseException {
131+
final String methodTag = TAG + ":execute";
132+
133+
if (mSupportsBoundService && (Thread.currentThread() == Looper.getMainLooper().getThread())) {
134+
throw new ClientException(ClientException.CALLED_ON_MAIN_THREAD,
135+
protocolParameters.getProtocolName() + " must not be called from the main thread.");
136+
}
137+
138+
final Queue<BrokerCommunicationException> communicationExceptionQueue = new LinkedList<>();
139+
final Span span = OTelUtility.createSpan(SpanName.DeviceRegistrationIpc.name());
140+
try (final Scope ignored = SpanExtension.makeCurrentSpan(span)) {
141+
span.setAttribute(AttributeName.device_registration_protocol_name.name(), protocolParameters.getProtocolName());
142+
span.setAttribute(AttributeName.calling_package_name.name(), mCallerPackageName);
143+
span.setAttribute(AttributeName.active_broker_package_name.name(), mActiveBrokerPackageName);
144+
for (final IIpcStrategy strategy : mIpcStrategies) {
145+
try {
146+
byte[] protocolResult = communicateProtocolWithStrategy(protocolParameters, strategy);
147+
setIpcStrategyTelemetryAttributes(strategy.getType(), "OK");
148+
span.setStatus(StatusCode.OK);
149+
return protocolResult;
150+
} catch (final BrokerCommunicationException communicationException) {
151+
setIpcStrategyTelemetryAttributes(strategy.getType(), communicationException.getMessage());
152+
// Fails to communicate to the broker. Try next strategy in list.
153+
communicationExceptionQueue.add(communicationException);
154+
}
155+
}
156+
// If we reach this section We've tried all the strategies...
157+
Logger.error(methodTag, "All IPC strategies to communicate with the broker have failed.", null);
158+
for (final BrokerCommunicationException e : communicationExceptionQueue) {
159+
Logger.error(methodTag, e.getMessage(), e);
160+
}
161+
throw new DeviceRegistrationException(
162+
DeviceRegistrationException.FAILED_TO_COMMUNICATE_WITH_BROKER_ERROR_CODE,
163+
DeviceRegistrationException.FAILED_TO_COMMUNICATE_WITH_BROKER_ERROR_MESSAGE
164+
);
165+
} catch (final Throwable throwable) {
166+
span.recordException(throwable);
167+
span.setStatus(StatusCode.ERROR);
168+
throw throwable;
169+
} finally {
170+
span.end();
171+
}
172+
}
173+
174+
/**
175+
* Communicates the protocol associated with the given parameters using the provided strategy.
176+
*
177+
* @param protocolParameters protocol parameters to execute.
178+
* @param ipcStrategy strategy to be invoked.
179+
* @return a serialized protocol response.
180+
* @throws BrokerCommunicationException if the strategy fails.
181+
*/
182+
@NonNull
183+
private byte[] communicateProtocolWithStrategy(
184+
@NonNull final IDeviceRegistrationProtocolParameters protocolParameters,
185+
@NonNull final IIpcStrategy ipcStrategy) throws BaseException {
186+
final String methodTag = TAG + ":executeProtocolWithStrategy";
187+
Logger.info(methodTag, "Executing " + protocolParameters.getProtocolName()
188+
+ " with strategy: " + ipcStrategy.getType());
189+
final Bundle protocolParametersBundle;
190+
try {
191+
protocolParametersBundle = mProtocolPacker.pack(protocolParameters);
192+
} catch (final Throwable throwable) {
193+
Logger.error(methodTag, "Serialization error while packing the protocol", throwable);
194+
throw new DeviceRegistrationException(
195+
DeviceRegistrationException.INTERNAL_ERROR_CODE,
196+
DeviceRegistrationException.SERIALIZATION_ERROR_MESSAGE,
197+
throwable
198+
);
199+
}
200+
final Bundle protocolResultBundle = ipcStrategy.communicateToBroker(
201+
new BrokerOperationBundle(
202+
DEVICE_REGISTRATION_OPERATIONS,
203+
mActiveBrokerPackageName,
204+
protocolParametersBundle
205+
)
206+
);
207+
208+
if (protocolResultBundle == null) {
209+
throw new ClientException(INVALID_BROKER_BUNDLE, "Broker Result not returned from Broker.");
210+
}
211+
212+
mCacheUpdater.updateCachedActiveBrokerFromResultBundle(protocolResultBundle);
213+
214+
return mProtocolPacker.unpackData(protocolResultBundle);
215+
}
216+
217+
/**
218+
* Records IPC strategy telemetry attributes on the current span for device registration.
219+
* <p>
220+
* Maps the strategy type to its corresponding status attribute and sets a human-readable
221+
* status message. If no attribute is mapped for the strategy type, a warning is logged and no
222+
* attribute is set. Callers should avoid passing sensitive data in the status message.
223+
* </p>
224+
*
225+
* @param strategyType {@link IIpcStrategy.Type} being evaluated.
226+
* @param statusMessage Status text describing the outcome; a fallback message is used when null.
227+
*/
228+
private void setIpcStrategyTelemetryAttributes(
229+
@NonNull final IIpcStrategy.Type strategyType,
230+
@Nullable final String statusMessage) {
231+
final String methodTag = TAG + ":setIpcStrategyTelemetryAttributes";
232+
final String attributeName;
233+
if (CONTENT_PROVIDER.equals(strategyType)) {
234+
attributeName = AttributeName.content_provider_status.name();
235+
} else if (BOUND_SERVICE.equals(strategyType)) {
236+
attributeName = AttributeName.bound_service_status.name();
237+
} else if (LEGACY_ACCOUNT_AUTHENTICATOR_FOR_WPJ_API.equals(strategyType)) {
238+
attributeName = AttributeName.legacy_account_manager_status.name();
239+
} else {
240+
attributeName = null;
241+
}
242+
if (attributeName == null) {
243+
Logger.warn(methodTag, "No attribute name mapped for strategy: " + strategyType.name());
244+
} else {
245+
final String message = statusMessage == null ? "No status message" : statusMessage;
246+
SpanExtension.current().setAttribute(attributeName, message);
247+
}
248+
}
249+
}

0 commit comments

Comments
 (0)