OVRGradleGeneration.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. /************************************************************************************
  2. Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
  3. Licensed under the Oculus SDK License Version 3.4.1 (the "License");
  4. you may not use the Oculus SDK except in compliance with the License,
  5. which is provided at the time of installation or download, or which
  6. otherwise accompanies this software in either electronic or hard copy form.
  7. You may obtain a copy of the License at
  8. https://developer.oculus.com/licenses/sdk-3.4.1
  9. Unless required by applicable law or agreed to in writing, the Oculus SDK
  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. //#define BUILDSESSION
  16. using System.Collections;
  17. using System.Collections.Generic;
  18. using System.IO;
  19. using System.Xml;
  20. using System.Diagnostics;
  21. using System.Threading;
  22. using UnityEditor;
  23. using UnityEditor.Android;
  24. using UnityEngine;
  25. using UnityEngine.Rendering;
  26. using UnityEditor.Build;
  27. #if UNITY_2018_1_OR_NEWER
  28. using UnityEditor.Build.Reporting;
  29. #endif
  30. using System;
  31. [InitializeOnLoad]
  32. public class OVRGradleGeneration
  33. #if UNITY_2018_2_OR_NEWER
  34. : IPreprocessBuildWithReport, IPostprocessBuildWithReport, IPostGenerateGradleAndroidProject
  35. {
  36. public OVRADBTool adbTool;
  37. public Process adbProcess;
  38. public int callbackOrder { get { return 3; } }
  39. static private System.DateTime buildStartTime;
  40. static private System.Guid buildGuid;
  41. #if UNITY_ANDROID
  42. private const string prefName = "OVRAutoIncrementVersionCode_Enabled";
  43. private const string menuItemAutoIncVersion = "Oculus/Tools/Auto Increment Version Code";
  44. static bool autoIncrementVersion = false;
  45. #endif
  46. static OVRGradleGeneration()
  47. {
  48. EditorApplication.delayCall += OnDelayCall;
  49. }
  50. static void OnDelayCall()
  51. {
  52. #if UNITY_ANDROID
  53. autoIncrementVersion = PlayerPrefs.GetInt(prefName, 0) != 0;
  54. Menu.SetChecked(menuItemAutoIncVersion, autoIncrementVersion);
  55. #endif
  56. }
  57. #if UNITY_ANDROID
  58. [MenuItem(menuItemAutoIncVersion)]
  59. static void ToggleUtilities()
  60. {
  61. autoIncrementVersion = !autoIncrementVersion;
  62. Menu.SetChecked(menuItemAutoIncVersion, autoIncrementVersion);
  63. int newValue = (autoIncrementVersion) ? 1 : 0;
  64. PlayerPrefs.SetInt(prefName, newValue);
  65. PlayerPrefs.Save();
  66. UnityEngine.Debug.Log("Auto Increment Version Code: " + autoIncrementVersion);
  67. }
  68. #endif
  69. public void OnPreprocessBuild(BuildReport report)
  70. {
  71. #if UNITY_ANDROID
  72. // Generate error when Vulkan is selected as the perferred graphics API, which is not currently supported in Unity XR
  73. if (!PlayerSettings.GetUseDefaultGraphicsAPIs(BuildTarget.Android))
  74. {
  75. GraphicsDeviceType[] apis = PlayerSettings.GetGraphicsAPIs(BuildTarget.Android);
  76. if (apis.Length >= 1 && apis[0] == GraphicsDeviceType.Vulkan)
  77. {
  78. throw new BuildFailedException("XR is currently not supported when using the Vulkan Graphics API. Please go to PlayerSettings and remove 'Vulkan' from the list of Graphics APIs.");
  79. }
  80. }
  81. #endif
  82. buildStartTime = System.DateTime.Now;
  83. buildGuid = System.Guid.NewGuid();
  84. if (!report.summary.outputPath.Contains("OVRGradleTempExport"))
  85. {
  86. OVRPlugin.SetDeveloperMode(OVRPlugin.Bool.True);
  87. OVRPlugin.AddCustomMetadata("build_type", "standard");
  88. }
  89. OVRPlugin.AddCustomMetadata("build_guid", buildGuid.ToString());
  90. OVRPlugin.AddCustomMetadata("target_platform", report.summary.platform.ToString());
  91. #if !UNITY_2019_3_OR_NEWER
  92. OVRPlugin.AddCustomMetadata("scripting_runtime_version", UnityEditor.PlayerSettings.scriptingRuntimeVersion.ToString());
  93. #endif
  94. if (report.summary.platform == UnityEditor.BuildTarget.StandaloneWindows
  95. || report.summary.platform == UnityEditor.BuildTarget.StandaloneWindows64)
  96. {
  97. OVRPlugin.AddCustomMetadata("target_oculus_platform", "rift");
  98. }
  99. #if BUILDSESSION
  100. StreamWriter writer = new StreamWriter("build_session", false);
  101. UnityEngine.Debug.LogFormat("Build Session: {0}", buildGuid.ToString());
  102. writer.WriteLine(buildGuid.ToString());
  103. writer.Close();
  104. #endif
  105. }
  106. public void OnPostGenerateGradleAndroidProject(string path)
  107. {
  108. UnityEngine.Debug.Log("OVRGradleGeneration triggered.");
  109. var targetOculusPlatform = new List<string>();
  110. if (OVRDeviceSelector.isTargetDeviceGearVrOrGo)
  111. {
  112. targetOculusPlatform.Add("geargo");
  113. }
  114. if (OVRDeviceSelector.isTargetDeviceQuest)
  115. {
  116. targetOculusPlatform.Add("quest");
  117. }
  118. OVRPlugin.AddCustomMetadata("target_oculus_platform", String.Join("_", targetOculusPlatform.ToArray()));
  119. UnityEngine.Debug.LogFormat(" GearVR or Go = {0} Quest = {1}", OVRDeviceSelector.isTargetDeviceGearVrOrGo, OVRDeviceSelector.isTargetDeviceQuest);
  120. bool isQuestOnly = OVRDeviceSelector.isTargetDeviceQuest && !OVRDeviceSelector.isTargetDeviceGearVrOrGo;
  121. if (isQuestOnly)
  122. {
  123. if (File.Exists(Path.Combine(path, "build.gradle")))
  124. {
  125. try
  126. {
  127. string gradle = File.ReadAllText(Path.Combine(path, "build.gradle"));
  128. int v2Signingindex = gradle.IndexOf("v2SigningEnabled false");
  129. if (v2Signingindex != -1)
  130. {
  131. gradle = gradle.Replace("v2SigningEnabled false", "v2SigningEnabled true");
  132. System.IO.File.WriteAllText(Path.Combine(path, "build.gradle"), gradle);
  133. }
  134. }
  135. catch (System.Exception e)
  136. {
  137. UnityEngine.Debug.LogWarningFormat("Unable to overwrite build.gradle, error {0}", e.Message);
  138. }
  139. }
  140. else
  141. {
  142. UnityEngine.Debug.LogWarning("Unable to locate build.gradle");
  143. }
  144. }
  145. PatchAndroidManifest(path);
  146. }
  147. public void PatchAndroidManifest(string path)
  148. {
  149. string manifestFolder = Path.Combine(path, "src/main");
  150. try
  151. {
  152. // Load android manfiest file
  153. XmlDocument doc = new XmlDocument();
  154. doc.Load(manifestFolder + "/AndroidManifest.xml");
  155. string androidNamepsaceURI;
  156. XmlElement element = (XmlElement)doc.SelectSingleNode("/manifest");
  157. if (element == null)
  158. {
  159. UnityEngine.Debug.LogError("Could not find manifest tag in android manifest.");
  160. return;
  161. }
  162. // Get android namespace URI from the manifest
  163. androidNamepsaceURI = element.GetAttribute("xmlns:android");
  164. if (!string.IsNullOrEmpty(androidNamepsaceURI))
  165. {
  166. // Look for intent filter category and change LAUNCHER to INFO
  167. XmlNodeList nodeList = doc.SelectNodes("/manifest/application/activity/intent-filter/category");
  168. foreach (XmlElement e in nodeList)
  169. {
  170. string attr = e.GetAttribute("name", androidNamepsaceURI);
  171. if (attr == "android.intent.category.LAUNCHER")
  172. {
  173. e.SetAttribute("name", androidNamepsaceURI, "android.intent.category.INFO");
  174. }
  175. }
  176. //If Quest is the target device, add the headtracking manifest tag
  177. if (OVRDeviceSelector.isTargetDeviceQuest)
  178. {
  179. XmlNodeList manifestUsesFeatureNodes = doc.SelectNodes("/manifest/uses-feature");
  180. bool foundHeadtrackingTag = false;
  181. foreach (XmlElement e in manifestUsesFeatureNodes)
  182. {
  183. string attr = e.GetAttribute("name", androidNamepsaceURI);
  184. if (attr == "android.hardware.vr.headtracking")
  185. foundHeadtrackingTag = true;
  186. }
  187. //If the tag already exists, don't patch with a new one. If it doesn't, we add it.
  188. if (!foundHeadtrackingTag)
  189. {
  190. XmlNode manifestElement = doc.SelectSingleNode("/manifest");
  191. XmlElement headtrackingTag = doc.CreateElement("uses-feature");
  192. headtrackingTag.SetAttribute("name", androidNamepsaceURI, "android.hardware.vr.headtracking");
  193. headtrackingTag.SetAttribute("version", androidNamepsaceURI, "1");
  194. string tagRequired = OVRDeviceSelector.isTargetDeviceGearVrOrGo ? "false" : "true";
  195. headtrackingTag.SetAttribute("required", androidNamepsaceURI, tagRequired);
  196. manifestElement.AppendChild(headtrackingTag);
  197. }
  198. }
  199. // If Quest is the target device, add the handtracking manifest tags if needed
  200. // Mapping of project setting to manifest setting:
  201. // OVRProjectConfig.HandTrackingSupport.ControllersOnly => manifest entry not present
  202. // OVRProjectConfig.HandTrackingSupport.ControllersAndHands => manifest entry present and required=false
  203. // OVRProjectConfig.HandTrackingSupport.HandsOnly => manifest entry present and required=true
  204. if (OVRDeviceSelector.isTargetDeviceQuest)
  205. {
  206. OVRProjectConfig.HandTrackingSupport targetHandTrackingSupport = OVRProjectConfig.GetProjectConfig().handTrackingSupport;
  207. bool handTrackingEntryNeeded = (targetHandTrackingSupport != OVRProjectConfig.HandTrackingSupport.ControllersOnly);
  208. if (handTrackingEntryNeeded)
  209. {
  210. // uses-feature: <uses-feature android:name="oculus.software.handtracking" android:required="false" />
  211. XmlNodeList manifestUsesFeatureNodes = doc.SelectNodes("/manifest/uses-feature");
  212. bool foundHandTrackingFeature = false;
  213. foreach (XmlElement e in manifestUsesFeatureNodes)
  214. {
  215. string attr = e.GetAttribute("name", androidNamepsaceURI);
  216. if (attr == "oculus.software.handtracking")
  217. foundHandTrackingFeature = true;
  218. }
  219. //If the tag already exists, don't patch with a new one. If it doesn't, we add it.
  220. if (!foundHandTrackingFeature)
  221. {
  222. XmlNode manifestElement = doc.SelectSingleNode("/manifest");
  223. XmlElement handTrackingFeature = doc.CreateElement("uses-feature");
  224. handTrackingFeature.SetAttribute("name", androidNamepsaceURI, "oculus.software.handtracking");
  225. string tagRequired = (targetHandTrackingSupport == OVRProjectConfig.HandTrackingSupport.HandsOnly) ? "true" : "false";
  226. handTrackingFeature.SetAttribute("required", androidNamepsaceURI, tagRequired);
  227. manifestElement.AppendChild(handTrackingFeature);
  228. }
  229. // uses-permission: <uses-permission android:name="oculus.permission.handtracking" />
  230. XmlNodeList manifestUsesPermissionNodes = doc.SelectNodes("/manifest/uses-permission");
  231. bool foundHandTrackingPermission = false;
  232. foreach (XmlElement e in manifestUsesPermissionNodes)
  233. {
  234. string attr = e.GetAttribute("name", androidNamepsaceURI);
  235. if (attr == "oculus.permission.handtracking")
  236. foundHandTrackingPermission = true;
  237. }
  238. //If the tag already exists, don't patch with a new one. If it doesn't, we add it.
  239. if (!foundHandTrackingPermission)
  240. {
  241. XmlNode manifestElement = doc.SelectSingleNode("/manifest");
  242. XmlElement handTrackingPermission = doc.CreateElement("uses-permission");
  243. handTrackingPermission.SetAttribute("name", androidNamepsaceURI, "oculus.permission.handtracking");
  244. manifestElement.AppendChild(handTrackingPermission);
  245. }
  246. }
  247. }
  248. XmlElement applicationNode = (XmlElement)doc.SelectSingleNode("/manifest/application");
  249. if(applicationNode != null)
  250. {
  251. // If android label and icon are missing from the xml, add them
  252. if (applicationNode.GetAttribute("android:label") == null)
  253. {
  254. applicationNode.SetAttribute("label", androidNamepsaceURI, "@string/app_name");
  255. }
  256. if (applicationNode.GetAttribute("android:icon") == null)
  257. {
  258. applicationNode.SetAttribute("icon", androidNamepsaceURI, "@mipmap/app_icon");
  259. }
  260. // Check for VR tag, if missing, append it
  261. bool vrTagFound = false;
  262. XmlNodeList appNodeList = applicationNode.ChildNodes;
  263. foreach (XmlElement e in appNodeList)
  264. {
  265. if (e.GetAttribute("android:name") == "com.samsung.android.vr.application.mode")
  266. {
  267. vrTagFound = true;
  268. break;
  269. }
  270. }
  271. if (!vrTagFound)
  272. {
  273. XmlElement vrTag = doc.CreateElement("meta-data");
  274. vrTag.SetAttribute("name", androidNamepsaceURI, "com.samsung.android.vr.application.mode");
  275. vrTag.SetAttribute("value", androidNamepsaceURI, "vr_only");
  276. applicationNode.AppendChild(vrTag); ;
  277. }
  278. // Disable allowBackup in manifest and add Android NSC XML file
  279. OVRProjectConfig projectConfig = OVRProjectConfig.GetProjectConfig();
  280. if (projectConfig != null)
  281. {
  282. if (projectConfig.disableBackups)
  283. {
  284. applicationNode.SetAttribute("allowBackup", androidNamepsaceURI, "false");
  285. }
  286. if (projectConfig.enableNSCConfig)
  287. {
  288. applicationNode.SetAttribute("networkSecurityConfig", androidNamepsaceURI, "@xml/network_sec_config");
  289. string securityConfigFile = Path.Combine(Application.dataPath, "Oculus/VR/Editor/network_sec_config.xml");
  290. string xmlDirectory = Path.Combine(path, "src/main/res/xml");
  291. try
  292. {
  293. if (!Directory.Exists(xmlDirectory))
  294. {
  295. Directory.CreateDirectory(xmlDirectory);
  296. }
  297. File.Copy(securityConfigFile, Path.Combine(xmlDirectory, "network_sec_config.xml"), true);
  298. }
  299. catch (Exception e)
  300. {
  301. UnityEngine.Debug.LogError(e.Message);
  302. }
  303. }
  304. }
  305. }
  306. doc.Save(manifestFolder + "/AndroidManifest.xml");
  307. }
  308. }
  309. catch (Exception e)
  310. {
  311. UnityEngine.Debug.LogError(e.Message);
  312. }
  313. }
  314. public void OnPostprocessBuild(BuildReport report)
  315. {
  316. #if UNITY_ANDROID
  317. if(autoIncrementVersion)
  318. {
  319. if((report.summary.options & BuildOptions.Development) == 0)
  320. {
  321. PlayerSettings.Android.bundleVersionCode++;
  322. UnityEngine.Debug.Log("Incrementing version code to " + PlayerSettings.Android.bundleVersionCode);
  323. }
  324. }
  325. bool isExporting = true;
  326. foreach (var step in report.steps)
  327. {
  328. if (step.name.Contains("Compile scripts")
  329. || step.name.Contains("Building scenes")
  330. || step.name.Contains("Writing asset files")
  331. || step.name.Contains("Preparing APK resources")
  332. || step.name.Contains("Creating Android manifest")
  333. || step.name.Contains("Processing plugins")
  334. || step.name.Contains("Exporting project")
  335. || step.name.Contains("Building Gradle project"))
  336. {
  337. OVRPlugin.SendEvent("build_step_" + step.name.ToLower().Replace(' ', '_'),
  338. step.duration.TotalSeconds.ToString(), "ovrbuild");
  339. #if BUILDSESSION
  340. UnityEngine.Debug.LogFormat("build_step_" + step.name.ToLower().Replace(' ', '_') + ": {0}", step.duration.TotalSeconds.ToString());
  341. #endif
  342. if(step.name.Contains("Building Gradle project"))
  343. {
  344. isExporting = false;
  345. }
  346. }
  347. }
  348. OVRPlugin.AddCustomMetadata("build_step_count", report.steps.Length.ToString());
  349. if (report.summary.outputPath.Contains("apk")) // Exclude Gradle Project Output
  350. {
  351. var fileInfo = new System.IO.FileInfo(report.summary.outputPath);
  352. OVRPlugin.AddCustomMetadata("build_output_size", fileInfo.Length.ToString());
  353. }
  354. #endif
  355. if (!report.summary.outputPath.Contains("OVRGradleTempExport"))
  356. {
  357. OVRPlugin.SendEvent("build_complete", (System.DateTime.Now - buildStartTime).TotalSeconds.ToString(), "ovrbuild");
  358. #if BUILDSESSION
  359. UnityEngine.Debug.LogFormat("build_complete: {0}", (System.DateTime.Now - buildStartTime).TotalSeconds.ToString());
  360. #endif
  361. }
  362. #if UNITY_ANDROID
  363. if (!isExporting)
  364. {
  365. // Get the hosts path to Android SDK
  366. if (adbTool == null)
  367. {
  368. adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath(false));
  369. }
  370. if (adbTool.isReady)
  371. {
  372. // Check to see if there are any ADB devices connected before continuing.
  373. List<string> devices = adbTool.GetDevices();
  374. if(devices.Count == 0)
  375. {
  376. return;
  377. }
  378. // Clear current logs on device
  379. Process adbClearProcess;
  380. adbClearProcess = adbTool.RunCommandAsync(new string[] { "logcat --clear" }, null);
  381. // Add a timeout if we cannot get a response from adb logcat --clear in time.
  382. Stopwatch timeout = new Stopwatch();
  383. timeout.Start();
  384. while (!adbClearProcess.WaitForExit(100))
  385. {
  386. if (timeout.ElapsedMilliseconds > 2000)
  387. {
  388. adbClearProcess.Kill();
  389. return;
  390. }
  391. }
  392. // Check if existing ADB process is still running, kill if needed
  393. if (adbProcess != null && !adbProcess.HasExited)
  394. {
  395. adbProcess.Kill();
  396. }
  397. // Begin thread to time upload and install
  398. var thread = new Thread(delegate ()
  399. {
  400. TimeDeploy();
  401. });
  402. thread.Start();
  403. }
  404. }
  405. #endif
  406. }
  407. #if UNITY_ANDROID
  408. public bool WaitForProcess;
  409. public bool TransferStarted;
  410. public DateTime UploadStart;
  411. public DateTime UploadEnd;
  412. public DateTime InstallEnd;
  413. public void TimeDeploy()
  414. {
  415. if (adbTool != null)
  416. {
  417. TransferStarted = false;
  418. DataReceivedEventHandler outputRecieved = new DataReceivedEventHandler(
  419. (s, e) =>
  420. {
  421. if (e.Data != null && e.Data.Length != 0 && !e.Data.Contains("\u001b"))
  422. {
  423. if (e.Data.Contains("free_cache"))
  424. {
  425. // Device recieved install command and is starting upload
  426. UploadStart = System.DateTime.Now;
  427. TransferStarted = true;
  428. }
  429. else if (e.Data.Contains("Running dexopt"))
  430. {
  431. // Upload has finished and Package Manager is starting install
  432. UploadEnd = System.DateTime.Now;
  433. }
  434. else if (e.Data.Contains("dex2oat took"))
  435. {
  436. // Package Manager finished install
  437. InstallEnd = System.DateTime.Now;
  438. WaitForProcess = false;
  439. }
  440. else if (e.Data.Contains("W PackageManager"))
  441. {
  442. // Warning from Package Manager is a failure in the install process
  443. WaitForProcess = false;
  444. }
  445. }
  446. }
  447. );
  448. WaitForProcess = true;
  449. adbProcess = adbTool.RunCommandAsync(new string[] { "logcat" }, outputRecieved);
  450. Stopwatch transferTimeout = new Stopwatch();
  451. transferTimeout.Start();
  452. while (adbProcess != null && !adbProcess.WaitForExit(100))
  453. {
  454. if (!WaitForProcess)
  455. {
  456. adbProcess.Kill();
  457. float UploadTime = (float)(UploadEnd - UploadStart).TotalMilliseconds / 1000f;
  458. float InstallTime = (float)(InstallEnd - UploadEnd).TotalMilliseconds / 1000f;
  459. if (UploadTime > 0f)
  460. {
  461. OVRPlugin.SendEvent("deploy_task", UploadTime.ToString(), "ovrbuild");
  462. }
  463. if (InstallTime > 0f)
  464. {
  465. OVRPlugin.SendEvent("install_task", InstallTime.ToString(), "ovrbuild");
  466. }
  467. }
  468. if (!TransferStarted && transferTimeout.ElapsedMilliseconds > 5000)
  469. {
  470. adbProcess.Kill();
  471. }
  472. }
  473. }
  474. }
  475. #endif
  476. #else
  477. {
  478. #endif
  479. }