1 /*
2  * Copyright (C) 2020 Google LLC
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 package com.google.carrier;
17 
18 import static java.nio.charset.StandardCharsets.UTF_8;
19 
20 import com.beust.jcommander.JCommander;
21 import com.beust.jcommander.Parameter;
22 import com.beust.jcommander.Parameters;
23 import com.google.carrier.CarrierSettings;
24 import com.google.carrier.MultiCarrierSettings;
25 import com.google.protobuf.ExtensionRegistry;
26 import com.google.protobuf.Message;
27 import com.google.protobuf.TextFormat;
28 import java.io.BufferedReader;
29 import java.io.BufferedWriter;
30 import java.io.File;
31 import java.io.IOException;
32 import java.io.OutputStream;
33 import java.nio.file.Files;
34 import java.nio.file.Paths;
35 
36 /**
37  * This command line tool generates device-specific settings from device overlay and base settings.
38  */
39 @Parameters(separators = "=")
40 public class GenDeviceSettings {
41   @Parameter(
42       names = "--version_offset",
43       description =
44           "The value to be added to file version, used to differentiate releases/branches.")
45   private long versionOffset = 0L;
46 
47   @Parameter(names = "--device_overlay", description = "The input device override textpb file.")
48   private String deviceFileName = "/tmp/device/muskie.textpb";
49 
50   @Parameter(names = "--base_setting_dir", description = "The path to input settings directory.")
51   private String baseSettingDirName = "/tmp/setting";
52 
53   @Parameter(
54       names = "--others_file",
55       description = "The file name of others carrier settings in the input directory.")
56   private String othersFileName = "others.textpb";
57 
58   @Parameter(
59       names = "--device_setting_dir",
60       description = "The path to putput <deice>_settings directory.")
61   private String deviceSettingDirName = "/tmp/muskie_setting";
62 
63   @Parameter(
64       names = "--with_version_number",
65       description = "Encode version number into output pb filename.")
66   private boolean versionInFileName = false;
67 
68   @Parameter(names = "--with_device_name", description = "Encode device name into output filename.")
69   private String deviceInFileName = "";
70 
71   private static final String PB_SUFFIX = ".pb";
72   private static final String TEXT_PB_SUFFIX = ".textpb";
73 
74   private static final ExtensionRegistry registry = ExtensionRegistry.newInstance();
75 
main(String[] args)76   public static void main(String[] args) throws IOException {
77     GenDeviceSettings generator = new GenDeviceSettings();
78     new JCommander(generator, args);
79     generator.generate();
80   }
81 
generate()82   private void generate() throws IOException {
83     // Load device overlay
84     MultiCarrierSettings deviceOverlay = null;
85     try (BufferedReader br = Files.newBufferedReader(Paths.get(deviceFileName), UTF_8)) {
86       MultiCarrierSettings.Builder builder = MultiCarrierSettings.newBuilder();
87       TextFormat.getParser().merge(br, registry, builder);
88       deviceOverlay = builder.build();
89     }
90 
91     // Create output settings directory if not exist.
92     File deviceSettingDir = new File(deviceSettingDirName);
93     if (!deviceSettingDir.exists()) {
94       deviceSettingDir.mkdirs();
95     }
96 
97     // For each carrier (and others) in baseSettingDir, find its overlay and apply.
98     File baseSettingDir = new File(baseSettingDirName);
99     for (String childName : baseSettingDir.list((dir, name) -> name.endsWith(TEXT_PB_SUFFIX))) {
100       System.out.println("Processing " + childName);
101 
102       File baseSettingFile = new File(baseSettingDir, childName);
103 
104       Message generatedMessage = null;
105       long version = 0L;
106 
107       if (othersFileName.equals(childName)) {
108 
109         // Load others setting
110         MultiCarrierSettings.Builder othersSetting = null;
111         try (BufferedReader br = Files.newBufferedReader(baseSettingFile.toPath(), UTF_8)) {
112           MultiCarrierSettings.Builder builder = MultiCarrierSettings.newBuilder();
113           TextFormat.getParser().merge(br, registry, builder);
114           othersSetting = builder;
115         }
116 
117         /*
118          * For non-tier1 carriers, DO NOT allow device overlay for now.
119          * There is no easy way to generate a mononical increasing version number with overlay.
120          * And if we do device overlay for a carrier, it should probobaly be tier-1.
121          */
122 
123         // Bump version according to the release
124         othersSetting.setVersion(
125             CarrierProtoUtils.addVersionOffset(othersSetting.getVersion(), versionOffset));
126 
127         // Convert vendor specific data into binary format
128         // Can be customized
129 
130         generatedMessage = othersSetting.build();
131         version = othersSetting.getVersion();
132 
133       } else { // a tier-1 carrier's setting
134 
135         // Load carrier setting
136         CarrierSettings.Builder carrierSetting = null;
137         try (BufferedReader br = Files.newBufferedReader(baseSettingFile.toPath(), UTF_8)) {
138           CarrierSettings.Builder builder = CarrierSettings.newBuilder();
139           TextFormat.getParser().merge(br, registry, builder);
140           carrierSetting = builder;
141         }
142 
143         // Apply device overlay
144         carrierSetting =
145             CarrierProtoUtils.applyDeviceOverlayToCarrierSettings(deviceOverlay, carrierSetting);
146 
147         // Bump version according to the release
148         carrierSetting.setVersion(
149             CarrierProtoUtils.addVersionOffset(carrierSetting.getVersion(), versionOffset));
150 
151         // Convert vendor specific data into binary format
152         // Can be customized
153 
154         generatedMessage = carrierSetting.build();
155         version = carrierSetting.getVersion();
156 
157       }
158 
159       // Output
160       String outFileMainName = childName.replace(TEXT_PB_SUFFIX, "");
161 
162       File deviceSettingTextFile = new File(deviceSettingDir, outFileMainName + TEXT_PB_SUFFIX);
163       try (BufferedWriter bw = Files.newBufferedWriter(deviceSettingTextFile.toPath(), UTF_8)) {
164         TextFormat.printUnicode(generatedMessage, bw);
165       }
166 
167       if (!deviceInFileName.isEmpty()) {
168         outFileMainName = deviceInFileName + "-" + outFileMainName;
169       }
170       if (versionInFileName) {
171         outFileMainName += "-" + version;
172       }
173       File deviceSettingFile = new File(deviceSettingDir, outFileMainName + PB_SUFFIX);
174       try (OutputStream os = Files.newOutputStream(deviceSettingFile.toPath())) {
175         generatedMessage.writeTo(os);
176       }
177     }
178   }
179 
GenDeviceSettings()180   private GenDeviceSettings() {}
181 }
182