blob: ab446b9f0f9d5de3a19e0cf426dba9632809b21e [file] [log] [blame]
Vojtech Bocek7e11ac52015-03-05 23:21:49 +01001#include <stdarg.h>
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5#include <fcntl.h>
6#include <sys/types.h>
7#include <time.h>
8#include <unistd.h>
9#include <stdlib.h>
10
11#include <string>
Aleksa Saraib25a1832015-12-31 17:36:00 +010012#include <sstream>
Vojtech Bocek7e11ac52015-03-05 23:21:49 +010013
14extern "C" {
15#include "../twcommon.h"
Vojtech Bocek7e11ac52015-03-05 23:21:49 +010016}
Ethan Yonkerfbb43532015-12-28 21:54:50 +010017#include "../minuitwrp/minui.h"
Sultan Qasim Khan7a48a662016-02-12 19:17:38 -050018#include "../twrp-functions.hpp"
Vojtech Bocek7e11ac52015-03-05 23:21:49 +010019#include "rapidxml.hpp"
20#include "objects.hpp"
21
22GUIPatternPassword::GUIPatternPassword(xml_node<>* node)
23 : GUIObject(node)
24{
25 xml_attribute<>* attr;
26 xml_node<>* child;
27
Aleksa Saraib25a1832015-12-31 17:36:00 +010028 // 3x3 is the default.
29 mGridSize = 3;
30 mDots = new Dot[mGridSize * mGridSize];
31 mConnectedDots = new int[mGridSize * mGridSize];
32
Vojtech Bocek7e11ac52015-03-05 23:21:49 +010033 ResetActiveDots();
34 mTrackingTouch = false;
35 mNeedRender = true;
36
37 ConvertStrToColor("blue", &mDotColor);
38 ConvertStrToColor("white", &mActiveDotColor);
39 ConvertStrToColor("blue", &mLineColor);
40
41 mDotImage = mActiveDotImage = NULL;
42 mDotCircle = mActiveDotCircle = NULL;
43 mDotRadius = 50;
44 mLineWidth = 35;
45
46 mAction = NULL;
47 mUpdate = 0;
48
49 if (!node)
50 return;
51
52 LoadPlacement(FindNode(node, "placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH, &mPlacement);
53
54 mAction = new GUIAction(node);
55
56 child = FindNode(node, "dot");
57 if(child)
58 {
59 mDotColor = LoadAttrColor(child, "color", mDotColor);
60 mActiveDotColor = LoadAttrColor(child, "activecolor", mActiveDotColor);
61 mDotRadius = LoadAttrIntScaleX(child, "radius", mDotRadius);
62
63 mDotImage = LoadAttrImage(child, "image");
64 mActiveDotImage = LoadAttrImage(child, "activeimage");
65 }
66
67 child = FindNode(node, "line");
68 if(child)
69 {
70 mLineColor = LoadAttrColor(child, "color", mLineColor);
71 mLineWidth = LoadAttrIntScaleX(child, "width", mLineWidth);
72 }
73
74 child = FindNode(node, "data");
75 if(child)
76 mPassVar = LoadAttrString(child, "name", "");
77
Aleksa Saraib25a1832015-12-31 17:36:00 +010078 child = FindNode(node, "size");
79 if(child) {
80 mSizeVar = LoadAttrString(child, "name", "");
81
82 // Use the configured default, if set.
83 size_t size = LoadAttrInt(child, "default", mGridSize);
84 Resize(size);
85 }
Vojtech Bocek7e11ac52015-03-05 23:21:49 +010086
87 if(!mDotImage || !mDotImage->GetResource() || !mActiveDotImage || !mActiveDotImage->GetResource())
88 {
89 mDotCircle = gr_render_circle(mDotRadius, mDotColor.red, mDotColor.green, mDotColor.blue, mDotColor.alpha);
90 mActiveDotCircle = gr_render_circle(mDotRadius/2, mActiveDotColor.red, mActiveDotColor.green, mActiveDotColor.blue, mActiveDotColor.alpha);
91 }
92 else
93 mDotRadius = mDotImage->GetWidth()/2;
94
95 SetRenderPos(mRenderX, mRenderY, mRenderW, mRenderH);
96}
97
98GUIPatternPassword::~GUIPatternPassword()
99{
100 delete mDotImage;
101 delete mActiveDotImage;
102 delete mAction;
103
Aleksa Saraib25a1832015-12-31 17:36:00 +0100104 delete[] mDots;
105 delete[] mConnectedDots;
106
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100107 if(mDotCircle)
108 gr_free_surface(mDotCircle);
109
110 if(mActiveDotCircle)
111 gr_free_surface(mActiveDotCircle);
112}
113
114void GUIPatternPassword::ResetActiveDots()
115{
116 mConnectedDotsLen = 0;
117 mCurLineX = mCurLineY = -1;
Aleksa Saraib25a1832015-12-31 17:36:00 +0100118 for(size_t i = 0; i < mGridSize * mGridSize; ++i)
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100119 mDots[i].active = false;
120}
121
122int GUIPatternPassword::SetRenderPos(int x, int y, int w, int h)
123{
124 mRenderX = x;
125 mRenderY = y;
126
127 if (w || h)
128 {
129 mRenderW = w;
130 mRenderH = h;
131
132 mAction->SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
133 SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
134 }
135
136 CalculateDotPositions();
137 return 0;
138}
139
140void GUIPatternPassword::CalculateDotPositions(void)
141{
Aleksa Saraib25a1832015-12-31 17:36:00 +0100142 const int num_gaps = mGridSize - 1;
143 const int step_x = (mRenderW - mDotRadius*2) / num_gaps;
144 const int step_y = (mRenderH - mDotRadius*2) / num_gaps;
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100145 int x = mRenderX;
146 int y = mRenderY;
147
Aleksa Saraib25a1832015-12-31 17:36:00 +0100148 /* Order is important for keyphrase generation:
149 *
150 * 0 1 2 3 ... n-1
151 * n n+1 n+2 n+3 ... 2n-1
152 * 2n 2n+1 2n+2 2n+3 ... 3n-1
153 * 3n 3n+1 3n+2 3n+3 ... 4n-1
154 * : : : :
155 * n*n-1
156 */
157
158 for(size_t r = 0; r < mGridSize; ++r)
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100159 {
Aleksa Saraib25a1832015-12-31 17:36:00 +0100160 for(size_t c = 0; c < mGridSize; ++c)
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100161 {
Aleksa Saraib25a1832015-12-31 17:36:00 +0100162 mDots[mGridSize*r + c].x = x;
163 mDots[mGridSize*r + c].y = y;
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100164 x += step_x;
165 }
166 x = mRenderX;
167 y += step_y;
168 }
169}
170
171int GUIPatternPassword::Render(void)
172{
173 if(!isConditionTrue())
174 return 0;
175
176 gr_color(mLineColor.red, mLineColor.green, mLineColor.blue, mLineColor.alpha);
177 for(size_t i = 1; i < mConnectedDotsLen; ++i) {
178 const Dot& dp = mDots[mConnectedDots[i-1]];
179 const Dot& dc = mDots[mConnectedDots[i]];
180 gr_line(dp.x + mDotRadius, dp.y + mDotRadius, dc.x + mDotRadius, dc.y + mDotRadius, mLineWidth);
181 }
182
183 if(mConnectedDotsLen > 0 && mTrackingTouch) {
184 const Dot& dc = mDots[mConnectedDots[mConnectedDotsLen-1]];
185 gr_line(dc.x + mDotRadius, dc.y + mDotRadius, mCurLineX, mCurLineY, mLineWidth);
186 }
187
Aleksa Saraib25a1832015-12-31 17:36:00 +0100188 for(size_t i = 0; i < mGridSize * mGridSize; ++i) {
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100189 if(mDotCircle) {
190 gr_blit(mDotCircle, 0, 0, gr_get_width(mDotCircle), gr_get_height(mDotCircle), mDots[i].x, mDots[i].y);
191 if(mDots[i].active) {
192 gr_blit(mActiveDotCircle, 0, 0, gr_get_width(mActiveDotCircle), gr_get_height(mActiveDotCircle), mDots[i].x + mDotRadius/2, mDots[i].y + mDotRadius/2);
193 }
194 } else {
195 if(mDots[i].active) {
196 gr_blit(mActiveDotImage->GetResource(), 0, 0, mActiveDotImage->GetWidth(), mActiveDotImage->GetHeight(),
197 mDots[i].x + (mDotRadius - mActiveDotImage->GetWidth()/2), mDots[i].y + (mDotRadius - mActiveDotImage->GetHeight()/2));
198 } else {
199 gr_blit(mDotImage->GetResource(), 0, 0, mDotImage->GetWidth(), mDotImage->GetHeight(), mDots[i].x, mDots[i].y);
200 }
201 }
202 }
203 return 0;
204}
205
206int GUIPatternPassword::Update(void)
207{
208 if(!isConditionTrue())
209 return 0;
210
211 int res = mNeedRender ? 2 : 1;
212 mNeedRender = false;
213 return res;
214}
215
Aleksa Saraib25a1832015-12-31 17:36:00 +0100216void GUIPatternPassword::Resize(size_t n) {
217 if(mGridSize == n)
218 return;
219
220 delete[] mDots;
221 delete[] mConnectedDots;
222
223 mGridSize = n;
224 mDots = new Dot[n*n];
225 mConnectedDots = new int[n*n];
226
227 ResetActiveDots();
228 CalculateDotPositions();
229 mTrackingTouch = false;
230 mNeedRender = true;
231}
232
233static int pow(int x, int i)
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100234{
nailyk61c7c5c2016-08-19 13:36:07 +0200235 int result = 1;
236 if (i<0)
237 return 0;
238 while(i-- > 0)
239 result *= x;
240 return result;
Aleksa Saraib25a1832015-12-31 17:36:00 +0100241}
242
243static bool IsInCircle(int x, int y, int ox, int oy, int r)
244{
245 return pow(x - ox, 2) + pow(y - oy, 2) <= pow(r, 2);
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100246}
247
248int GUIPatternPassword::InDot(int x, int y)
249{
Aleksa Saraib25a1832015-12-31 17:36:00 +0100250 for(size_t i = 0; i < mGridSize * mGridSize; ++i) {
251 if(IsInCircle(x, y, mDots[i].x + mDotRadius, mDots[i].y + mDotRadius, mDotRadius*3))
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100252 return i;
253 }
254 return -1;
255}
256
257bool GUIPatternPassword::DotUsed(int dot_idx)
258{
259 for(size_t i = 0; i < mConnectedDotsLen; ++i) {
260 if(mConnectedDots[i] == dot_idx)
261 return true;
262 }
263 return false;
264}
265
266void GUIPatternPassword::ConnectDot(int dot_idx)
267{
Aleksa Saraib25a1832015-12-31 17:36:00 +0100268 if(mConnectedDotsLen >= mGridSize * mGridSize)
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100269 {
270 LOGERR("mConnectedDots in GUIPatternPassword has overflown!\n");
271 return;
272 }
273
274 mConnectedDots[mConnectedDotsLen++] = dot_idx;
275 mDots[dot_idx].active = true;
276}
277
Aleksa Saraib25a1832015-12-31 17:36:00 +0100278void GUIPatternPassword::ConnectIntermediateDots(int next_dot_idx)
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100279{
280 if(mConnectedDotsLen == 0)
281 return;
282
Aleksa Saraib25a1832015-12-31 17:36:00 +0100283 const int prev_dot_idx = mConnectedDots[mConnectedDotsLen-1];
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100284
Aleksa Saraib25a1832015-12-31 17:36:00 +0100285 int px = prev_dot_idx % mGridSize;
286 int py = prev_dot_idx / mGridSize;
287
288 int nx = next_dot_idx % mGridSize;
289 int ny = next_dot_idx / mGridSize;
290
291 /*
292 * We connect all dots that are in a straight line between the previous dot
293 * and the next one. This is simple for 3x3, but is more complicated for
294 * larger grids.
295 *
296 * Weirdly, Android doesn't do the logical thing when it comes to connecting
297 * dots between two points. Rather than simply adding all points that lie
298 * on the line between the start and end points, it instead only connects
299 * dots that are adjacent in only three directions -- horizontal, vertical
300 * and diagonal (45°).
301 *
302 * So we can just iterate over the correct axes, taking care to ensure that
303 * the order in which the intermediate points are added to the pattern is
304 * correct.
305 */
306
307 int x = px;
308 int y = py;
309
310 int Dx = (nx > px) ? 1 : -1;
311 int Dy = (ny > py) ? 1 : -1;
312
313 // Vertical lines.
314 if(px == nx)
315 Dx = 0;
316
317 // Horizontal lines.
318 else if(py == ny)
319 Dy = 0;
320
321 // Diagonal lines (|∆x| = |∆y|).
322 else if(abs(px - nx) == abs(py - ny))
323 ;
324
325 // No valid intermediate dots.
326 else
Vojtech Boceke8c39272015-03-15 15:25:37 +0100327 return;
Vojtech Boceke8c39272015-03-15 15:25:37 +0100328
Aleksa Saraib25a1832015-12-31 17:36:00 +0100329 // Iterate along axis, adding dots in the correct order.
330 while((Dy == 0 || y != ny - Dy) && (Dx == 0 || x != nx - Dx)) {
331 x += Dx;
332 y += Dy;
333
334 int idx = mGridSize * y + x;
335 if(!DotUsed(idx))
336 ConnectDot(idx);
337 }
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100338}
339
340int GUIPatternPassword::NotifyTouch(TOUCH_STATE state, int x, int y)
341{
342 if(!isConditionTrue())
343 return -1;
344
345 switch (state)
346 {
347 case TOUCH_START:
348 {
349 const int dot_idx = InDot(x, y);
350 if(dot_idx == -1)
351 break;
352
353 mTrackingTouch = true;
354 ResetActiveDots();
355 ConnectDot(dot_idx);
356 DataManager::Vibrate("tw_button_vibrate");
357 mCurLineX = x;
358 mCurLineY = y;
359 mNeedRender = true;
360 break;
361 }
362 case TOUCH_DRAG:
363 {
364 if(!mTrackingTouch)
365 break;
366
367 const int dot_idx = InDot(x, y);
368 if(dot_idx != -1 && !DotUsed(dot_idx))
369 {
370 ConnectIntermediateDots(dot_idx);
371 ConnectDot(dot_idx);
372 DataManager::Vibrate("tw_button_vibrate");
373 }
374
375 mCurLineX = x;
376 mCurLineY = y;
377 mNeedRender = true;
378 break;
379 }
380 case TOUCH_RELEASE:
381 {
382 if(!mTrackingTouch)
383 break;
384
385 mNeedRender = true;
386 mTrackingTouch = false;
387 PatternDrawn();
388 ResetActiveDots();
389 break;
390 }
391 default:
392 break;
393 }
394 return 0;
395}
396
Aleksa Saraib25a1832015-12-31 17:36:00 +0100397int GUIPatternPassword::NotifyVarChange(const std::string& varName, const std::string& value)
398{
399 if(!isConditionTrue())
400 return 0;
401
402 if(varName == mSizeVar) {
403 Resize(atoi(value.c_str()));
404 mUpdate = true;
405 }
406 return 0;
407}
408
Sultan Qasim Khan7a48a662016-02-12 19:17:38 -0500409static unsigned int getSDKVersion(void) {
410 unsigned int sdkver = 23;
411 string sdkverstr = TWFunc::System_Property_Get("ro.build.version.sdk");
412 if (!sdkverstr.empty()) {
413 sdkver = (unsigned int)strtoull(sdkverstr.c_str(), NULL, 10);
414 sdkver = (sdkver != 0) ? sdkver : 23;
415 }
416 LOGINFO("sdk version is %u\n", sdkver);
417 return sdkver;
418}
419
Aleksa Saraib25a1832015-12-31 17:36:00 +0100420std::string GUIPatternPassword::GeneratePassphrase()
421{
422 char pattern[mConnectedDotsLen];
423 for(size_t i = 0; i < mConnectedDotsLen; i++) {
424 pattern[i] = (char) mConnectedDots[i];
425 }
426
427 std::stringstream pass;
Sultan Qasim Khan7a48a662016-02-12 19:17:38 -0500428 char buffer[3] = {0};
Aleksa Saraib25a1832015-12-31 17:36:00 +0100429
Sultan Qasim Khan7a48a662016-02-12 19:17:38 -0500430 if ((mGridSize == 3) || (getSDKVersion() >= 23)) {
431 // Marshmallow uses a consistent method
432 for (size_t i = 0; i < mConnectedDotsLen; i++) {
433 buffer[0] = (pattern[i] & 0xff) + '1';
434 pass << std::string(buffer);
435 }
436 } else {
Aleksa Saraib25a1832015-12-31 17:36:00 +0100437 /*
Sultan Qasim Khan7a48a662016-02-12 19:17:38 -0500438 * Okay, rant time for pre-Marshmallow ROMs.
Aleksa Saraib25a1832015-12-31 17:36:00 +0100439 * It turns out that Android and CyanogenMod have *two* separate methods
440 * for generating passphrases from patterns. This is a legacy issue, as
441 * Android only supports 3x3 grids, and so we need to support both.
442 * Luckily, CyanogenMod is in the same boat as us and needs to support
443 * Android's 3x3 encryption style.
444 *
445 * In order to generate a 3x3 passphrase, add 1 to each dot index
446 * and concatenate the string representation of the integers. No
447 * padding should be added.
448 *
449 * For *all* other NxN passphrases (until a 16x16 grid comes along),
450 * they are generated by taking "%.2x" for each dot index and
451 * concatenating the results (without adding 1).
452 */
Sultan Qasim Khan7a48a662016-02-12 19:17:38 -0500453 for (size_t i = 0; i < mConnectedDotsLen; i++) {
454 snprintf(buffer, 3, "%.2x", pattern[i] & 0xff);
Aleksa Saraib25a1832015-12-31 17:36:00 +0100455 pass << std::string(buffer);
456 }
457 }
458
459 return pass.str();
460}
461
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100462void GUIPatternPassword::PatternDrawn()
463{
464 if(!mPassVar.empty() && mConnectedDotsLen > 0)
Aleksa Saraib25a1832015-12-31 17:36:00 +0100465 DataManager::SetValue(mPassVar, GeneratePassphrase());
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100466
467 if(mAction)
468 mAction->doActions();
469}