Skip to content

Commit 200c0a0

Browse files
authored
Library Version implementation. (#203)
The change adds a mechanism for libraries, sdks to register their versions with the runtime.
1 parent 819d738 commit 200c0a0

File tree

13 files changed

+440
-4
lines changed

13 files changed

+440
-4
lines changed

firebase-common/firebase-common.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ dependencies {
5656
implementation "com.google.android.gms:play-services-basement:$playServicesVersion"
5757
implementation "com.google.android.gms:play-services-tasks:$playServicesVersion"
5858

59+
api 'com.google.auto.value:auto-value-annotations:1.6'
5960
compileOnly 'com.google.code.findbugs:jsr305:3.0.2'
6061

6162
testImplementation 'com.android.support.test:runner:1.0.2'
@@ -64,6 +65,8 @@ dependencies {
6465
testImplementation "com.google.truth:truth:$googleTruthVersion"
6566
testImplementation 'org.mockito:mockito-core:2.21.0'
6667

68+
annotationProcessor 'com.google.auto.value:auto-value:1.6'
69+
6770
androidTestImplementation 'junit:junit:4.12'
6871
androidTestImplementation 'com.android.support.test:runner:1.0.2'
6972
androidTestImplementation "com.google.truth:truth:$googleTruthVersion"

firebase-common/src/androidTest/java/com/google/firebase/FirebaseAppTest.java

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@
4343
import com.google.firebase.components.InitTracker;
4444
import com.google.firebase.components.TestComponentOne;
4545
import com.google.firebase.components.TestComponentTwo;
46+
import com.google.firebase.components.TestUserAgentDependentComponent;
4647
import com.google.firebase.internal.InternalTokenResult;
48+
import com.google.firebase.platforminfo.UserAgentPublisher;
4749
import com.google.firebase.testing.FirebaseAppRule;
4850
import java.lang.reflect.InvocationTargetException;
4951
import java.lang.reflect.Method;
@@ -65,10 +67,8 @@
6567
// TODO(arondeak): uncomment lines when Firebase API targets are in integ.
6668
@RunWith(AndroidJUnit4.class)
6769
public class FirebaseAppTest {
68-
6970
protected static final String GOOGLE_APP_ID = "1:855246033427:android:6e48bff8253f3f6e6e";
7071
protected static final String GOOGLE_API_KEY = "AIzaSyD3asb-2pEZVqMkmL6M9N6nHZRR_znhrh0";
71-
private static final String APP_NAME = "myApp";
7272

7373
protected static final FirebaseOptions OPTIONS =
7474
new FirebaseOptions.Builder()
@@ -118,6 +118,37 @@ public void testBackgroundStateChangeCallbacks() {
118118
assertThat(backgroundState.get()).isFalse();
119119
}
120120

121+
@Test
122+
public void testInitializeApp_shouldPublishUserAgentPublisherThatReturnsPublishedVersions() {
123+
String[] expectedUserAgent = {"firebase-common/16.0.5", "test-component/1.2.3"};
124+
Context mockContext = createForwardingMockContext();
125+
FirebaseApp firebaseApp = FirebaseApp.initializeApp(mockContext);
126+
127+
TestUserAgentDependentComponent userAgentDependant =
128+
firebaseApp.get(TestUserAgentDependentComponent.class);
129+
UserAgentPublisher userAgentPublisher = userAgentDependant.getUserAgentPublisher();
130+
String[] actualUserAgent = userAgentPublisher.getUserAgent().split(" ");
131+
Arrays.sort(actualUserAgent);
132+
133+
assertThat(actualUserAgent).asList().contains("test-component/1.2.3");
134+
}
135+
136+
@Test
137+
public void testInitializeApp_shouldPublishVersionForFirebaseCommon() {
138+
Context mockContext = createForwardingMockContext();
139+
FirebaseApp firebaseApp = FirebaseApp.initializeApp(mockContext);
140+
141+
TestUserAgentDependentComponent userAgentDependant =
142+
firebaseApp.get(TestUserAgentDependentComponent.class);
143+
UserAgentPublisher userAgentPublisher = userAgentDependant.getUserAgentPublisher();
144+
String[] actualUserAgent = userAgentPublisher.getUserAgent().split(" ");
145+
Arrays.sort(actualUserAgent);
146+
147+
// After sorting the user agents are expected to be {"firebase-common/x.y.z",
148+
// "test-component/1.2.3"}
149+
assertThat(actualUserAgent[0]).contains("firebase-common");
150+
}
151+
121152
@Test
122153
public void testRemovedBackgroundStateChangeCallbacksDontFire() {
123154
FirebaseApp firebaseApp = FirebaseApp.initializeApp(targetContext, OPTIONS, "myApp");

firebase-common/src/main/java/com/google/firebase/FirebaseApp.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@
5555
import com.google.firebase.internal.DefaultIdTokenListenersCountChangedListener;
5656
import com.google.firebase.internal.InternalTokenProvider;
5757
import com.google.firebase.internal.InternalTokenResult;
58+
import com.google.firebase.platforminfo.DefaultUserAgentPublisher;
59+
import com.google.firebase.platforminfo.LibraryVersionComponent;
5860
import java.lang.reflect.InvocationTargetException;
5961
import java.lang.reflect.Method;
6062
import java.lang.reflect.Modifier;
@@ -151,6 +153,8 @@ public class FirebaseApp {
151153
@GuardedBy("LOCK")
152154
static final Map<String, FirebaseApp> INSTANCES = new ArrayMap<>();
153155

156+
private static final String FIREBASE_COMMON = "firebase-common";
157+
154158
private final Context applicationContext;
155159
private final String name;
156160
private final FirebaseOptions options;
@@ -536,7 +540,9 @@ protected FirebaseApp(Context applicationContext, String name, FirebaseOptions o
536540
registrars,
537541
Component.of(applicationContext, Context.class),
538542
Component.of(this, FirebaseApp.class),
539-
Component.of(options, FirebaseOptions.class));
543+
Component.of(options, FirebaseOptions.class),
544+
LibraryVersionComponent.create(FIREBASE_COMMON, BuildConfig.VERSION_NAME),
545+
DefaultUserAgentPublisher.component());
540546
publisher = componentRuntime.get(Publisher.class);
541547
}
542548

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright 2018 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.platforminfo;
16+
17+
import com.google.firebase.components.Component;
18+
import com.google.firebase.components.Dependency;
19+
import java.util.Iterator;
20+
import java.util.Set;
21+
22+
/**
23+
* Provides a user agent string that captures the SDKs and their corresponding versions.
24+
*
25+
* <p>Example user agent string: "firebase-common/16.1.1 firebase-firestore/16.1.2
26+
* firebase-database/16.1.2"
27+
*/
28+
public class DefaultUserAgentPublisher implements UserAgentPublisher {
29+
private final String javaSDKVersionUserAgent;
30+
private final OutOfBandLibraryVersionRegistrar gamesSDKRegistrar;
31+
32+
DefaultUserAgentPublisher(
33+
Set<LibraryVersion> libraryVersions, OutOfBandLibraryVersionRegistrar gamesSDKRegistrar) {
34+
this.javaSDKVersionUserAgent = toUserAgent(libraryVersions);
35+
this.gamesSDKRegistrar = gamesSDKRegistrar;
36+
}
37+
38+
/**
39+
* Returns the user agent string that is computed as follows 1. For our JavaSDKs, the string is
40+
* computed in advance since the components framework guarantees that we receive all published
41+
* versions. 2. For our GamesSDKs, the strings are recomputed each time since the registration of
42+
* the versions happens out of band and we take the optimistic approach of recomputing each time.
43+
*/
44+
@Override
45+
public String getUserAgent() {
46+
if (gamesSDKRegistrar.getRegisteredVersions().isEmpty()) {
47+
return javaSDKVersionUserAgent;
48+
}
49+
50+
return javaSDKVersionUserAgent + ' ' + toUserAgent(gamesSDKRegistrar.getRegisteredVersions());
51+
}
52+
53+
private static String toUserAgent(Set<LibraryVersion> tokens) {
54+
StringBuilder sb = new StringBuilder();
55+
Iterator<LibraryVersion> iterator = tokens.iterator();
56+
while (iterator.hasNext()) {
57+
LibraryVersion token = iterator.next();
58+
sb.append(token.getLibraryName()).append('/').append(token.getVersion());
59+
if (iterator.hasNext()) {
60+
sb.append(' ');
61+
}
62+
}
63+
return sb.toString();
64+
}
65+
66+
/** Creates a component to codify a user agent string that captures SDK versions. */
67+
public static Component<UserAgentPublisher> component() {
68+
return Component.builder(UserAgentPublisher.class)
69+
.add(Dependency.setOf(LibraryVersion.class))
70+
.factory(
71+
c ->
72+
new DefaultUserAgentPublisher(
73+
c.setOf(LibraryVersion.class), OutOfBandLibraryVersionRegistrar.getInstance()))
74+
.build();
75+
}
76+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2018 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.platforminfo;
16+
17+
import com.google.auto.value.AutoValue;
18+
import javax.annotation.Nonnull;
19+
20+
/** The class is not public to ensure other components cannot depend on it. */
21+
@AutoValue
22+
abstract class LibraryVersion {
23+
static LibraryVersion create(String name, String version) {
24+
return new AutoValue_LibraryVersion(name, version);
25+
}
26+
27+
@Nonnull
28+
public abstract String getLibraryName();
29+
30+
@Nonnull
31+
public abstract String getVersion();
32+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2018 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.platforminfo;
16+
17+
import com.google.firebase.components.Component;
18+
19+
/** Factory to create a component that publishes the version of an SDK */
20+
public class LibraryVersionComponent {
21+
private LibraryVersionComponent() {}
22+
23+
/** Creates a component that publishes SDK versions */
24+
public static Component<?> create(String sdkName, String version) {
25+
return Component.intoSet(LibraryVersion.create(sdkName, version), LibraryVersion.class);
26+
}
27+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright 2018 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.platforminfo;
16+
17+
import java.util.Collections;
18+
import java.util.HashSet;
19+
import java.util.Set;
20+
21+
/**
22+
* In order to allow the C++ and Unity SDKs to publish their versions without the use of the
23+
* components framework, we have a mechanism where the versions can be wired as out of band as side
24+
* effects. See {@link OutOfBandLibraryVersionRegistrar#registerVersion(String, String)}
25+
*
26+
* <p>Java libraries should use {@link LibraryVersionComponent#create(String, String)} instead.
27+
*/
28+
public class OutOfBandLibraryVersionRegistrar {
29+
private final Set<LibraryVersion> infos = new HashSet<>();
30+
private static volatile OutOfBandLibraryVersionRegistrar INSTANCE;
31+
32+
OutOfBandLibraryVersionRegistrar() {}
33+
34+
/**
35+
* Thread safe method to publish versions outside of the components mechanics.
36+
*
37+
* <p>It is the responsibility of the caller to register the version at app launch.
38+
*/
39+
public void registerVersion(String sdkName, String version) {
40+
synchronized (infos) {
41+
infos.add(LibraryVersion.create(sdkName, version));
42+
}
43+
}
44+
45+
/** Returns registered versions */
46+
Set<LibraryVersion> getRegisteredVersions() {
47+
synchronized (infos) {
48+
return Collections.unmodifiableSet(infos);
49+
}
50+
}
51+
52+
/** Returns an instance of {@link OutOfBandLibraryVersionRegistrar} */
53+
public static OutOfBandLibraryVersionRegistrar getInstance() {
54+
OutOfBandLibraryVersionRegistrar localRef = INSTANCE;
55+
if (localRef == null) {
56+
synchronized (OutOfBandLibraryVersionRegistrar.class) {
57+
localRef = INSTANCE;
58+
if (localRef == null) {
59+
INSTANCE = localRef = new OutOfBandLibraryVersionRegistrar();
60+
}
61+
}
62+
}
63+
return localRef;
64+
}
65+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2018 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.platforminfo;
16+
17+
/** Component that publishes a user agent string */
18+
public interface UserAgentPublisher {
19+
String getUserAgent();
20+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2018 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
/** @hide */
16+
package com.google.firebase.platforminfo;

0 commit comments

Comments
 (0)